Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple improvements to ambiguity reporting clarity #2766

Conversation

afonsolage
Copy link
Contributor

@afonsolage afonsolage commented Sep 2, 2021

Objective

Simple improvements to ambiguity reporting clarity, as discussed on #1484.

Fixes #1484

Solution

  • 1. Number the reported ambiguities, to make them easier to discuss in a team.
  • 2. Display function signatures of conflicting systems. (see Simple improvements to ambiguity reporting clarity #2766 (comment))
  • 3. Explain which component(s) / resource(s) the systems are conflicting on.
  • 4. Report ambiguities where all pairwise combinations of systems conflict on the same data as a group.
  • 5. Create a simple .ambiguous() method that causes a system to be ignored completely in the ambiguity checker. This would only be useful for inconsequential systems like particle effect creation.
  • 6. Create a simple .ambiguous_with(system_label) method to create two-element ambiguity sets without being forced to invent an ambiguity set label.
  • 7. Provide an "X unresolved ambiguities detected" message that runs by default. Rather than toggling ambiguity detection on/off, have three states: off / minimal / verbose. This would provide discoverability that system ambiguities are a thing and let developers see when the change that they just made added / removed some ambiguities at a glance.
  • 8. Provide an way to ignore/filter crates when using ambiguity checker.
  • 9. By default, ambiguity checker should ignore internal bevy crates.
  • 10. Improve docs.
  • 11. Add an example of using ambiguity reporting

@github-actions github-actions bot added the S-Needs-Triage This issue needs to be labelled label Sep 2, 2021
@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use and removed S-Needs-Triage This issue needs to be labelled labels Sep 2, 2021
@alice-i-cecile
Copy link
Member

Thanks a ton for doing this! This should help a ton.

@davidscherer @concavesphere I'd love reviews from you two on this once it's ready; I think this will be a very nice incremental improvement to a friendlier scheduler.

@afonsolage
Copy link
Contributor Author

I wasn't able to find a reliable way to get the system signature as stated in task "Display function signatures of conflicting systems.".

Rust doesn't provide a way to get function signature and I wasn't able to do so using bevy internals. The most acceptable way was using component_access and using this to reconstruct the function signature, but it's not accurate, since the following function signature:

pub fn camera_system<T: CameraProjection + Component>(
    mut window_resized_events: EventReader<WindowResized>,
    mut window_created_events: EventReader<WindowCreated>,
    windows: Res<Windows>,
    mut queries: QuerySet<(
        QueryState<(Entity, &mut Camera, &mut T)>,
        QueryState<Entity, Added<Camera>>,
    )>,
) {

is reported as:

"bevy_render::camera::camera::camera_system<bevy_render::camera::projection::PerspectiveProjection>"("bevy_ecs::event::Events<bevy_window::event::WindowResized>", "bevy_ecs::event::Events<bevy_window::event::WindowCreated>", "bevy_window::windows::Windows", "bevy_render::camera::camera::Camera", "bevy_render::camera::projection::PerspectiveProjection")

If anyone knows a reliable way to reconstruct or get the system function signature, I'll be happy to address the task 2.

@afonsolage
Copy link
Contributor Author

The current implementation of task 7 (Provide an "X unresolved ambiguities detected" message that runs by default) currently produces the following output:

Sep 04 08:25:06.304  WARN bevy_ecs::schedule::stage: 1 unresolved ambiguities detected
Sep 04 08:25:06.306  WARN bevy_ecs::schedule::stage: 1 unresolved ambiguities detected
Sep 04 08:25:06.307  WARN bevy_ecs::schedule::stage: 12 unresolved ambiguities detected
Sep 04 08:25:06.341  WARN bevy_ecs::schedule::stage: 24 unresolved ambiguities detected
Sep 04 08:25:06.345  WARN bevy_ecs::schedule::stage: 3 unresolved ambiguities detected

This isn't very descriptive for new users, but I wasn't able to find a way to get the current stage label, so we would need to add this information on SystemStage in order to have a better output message, but I'm afraid this change would be out of the scope of this PR.

The following code currently produces the mentioned warnings:

use bevy::prelude::*;

fn main() {
    env_logger::init();
    App::new()
        .add_plugins(DefaultPlugins)
        .insert_resource(MyStartupResource(0))
        .add_startup_system(startup_system_a)
        .add_startup_system(startup_system_b)
        .insert_resource(MyParallelResource(0))
        .add_system(parallel_system_a)
        .add_system(parallel_system_b)
        .run();
}

struct MyStartupResource(i32);
fn startup_system_a(mut res: ResMut<MyStartupResource>) {
    res.0 += 1;
}
fn startup_system_b(mut res: ResMut<MyStartupResource>) {
    res.0 += 1;
}

struct MyParallelResource(i32);
fn parallel_system_a(mut res: ResMut<MyParallelResource>) {
    res.0 += 1;
}
fn parallel_system_b(mut res: ResMut<MyParallelResource>) {
    res.0 += 1;
}

@afonsolage
Copy link
Contributor Author

The main work is done, waiting for reviews. I'll just add those changes on ReportExecutionOrderAmbiguities docs and add a new example showing how to use those new features.

@@ -45,7 +52,39 @@ impl_downcast!(Stage);
///
/// The checker may report a system more times than the amount of constraints it would actually need
/// to have unambiguous order with regards to a group of already-constrained systems.
pub struct ReportExecutionOrderAmbiguities;
pub struct ReportExecutionOrderAmbiguities {
level: AmbiguityReportLevel,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be fine just exposing this field directly. That would save on boilerplate and the enum will give us a type-safe API.

Is there some reason why this isn't safe?

Copy link
Contributor Author

@afonsolage afonsolage Sep 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the latest commit, I made it public, so users which wanna more control over ReportExecutionOrderAmbiguities may create the resource directly. I've also kept the .off(), .minimal() and .verbose() to keep things easier to user (at least I think so)

pub struct ReportExecutionOrderAmbiguities {
    pub level: AmbiguityReportLevel,
    pub ignore_crates: Vec<String>,
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've also kept the .off(), .minimal() and .verbose() to keep things easier to user (at least I think so)

I'd like to avoid API duplication where possible. I'd definitely accept it if Cart feels otherwise in a final review though, so don't worry about it for now.

@afonsolage
Copy link
Contributor Author

I think everything is done, I'll set it as ready for review. I've added an example also. For reference, here is the output of the example:

Sep 04 14:04:15.223  WARN bevy_ecs::schedule::stage: 1 unresolved ambiguities detected. Set the level of the ReportExecutionOrderAmbiguities resource to AmbiguityReportLevel::Verbose for more details.
Sep 04 14:04:15.223  INFO bevy_ecs::schedule::stage: Execution order ambiguities detected, you might want to add an explicit dependency relation between some of these systems:
1. Parallel systems:
1.0 - Systems:
     ["ambiguity_checker::startup_system_a", "ambiguity_checker::startup_system_b"]
   - Conflicts on the following components/resources:
     ["ambiguity_checker::MyStartupResource"]

Sep 04 14:04:15.224  WARN bevy_ecs::schedule::stage: 4 unresolved ambiguities detected. Set the level of the ReportExecutionOrderAmbiguities resource to AmbiguityReportLevel::Verbose for more details.
Sep 04 14:04:15.224  INFO bevy_ecs::schedule::stage: Execution order ambiguities detected, you might want to add an explicit dependency relation between some of these systems:
1. Parallel systems:
1.0 - Systems:
     ["ambiguity_checker::system_b", "ambiguity_checker::system_d"]
   - Conflicts on the following components/resources:
     ["i32", "ambiguity_checker::MyResource"]
1.1 - Systems:
     ["ambiguity_checker::system_b", "ambiguity_checker::system_e", "ambiguity_checker::system_d", "ambiguity_checker::system_c"]
   - Conflicts on the following components/resources:
     ["ambiguity_checker::MyResource"]

@afonsolage afonsolage marked this pull request as ready for review September 4, 2021 17:05
}

/// Adds the given crates to be ignored by ambiguity checker. Check [`ReportExecutionOrderAmbiguities`] for more details.
pub fn ignore(mut self, create_prefix: &[&str]) -> Self {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should take a single string instead of a list. We could also have an "ignore_all" function that takes an iterator.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Renamed this one to ignore_all and added a new one ignore which just takes a single &str

pub ignore_crates: Vec<String>,
}

const BEVY_CRATES: [&str; 109] = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having thought about this a bit more, I think this is moving us in the wrong direction for a number of reasons:

  1. Moves "bevy engine" crate info "upstream" into bevy_ecs, which should be standalone
  2. Compiles a big list of strings into bevy_ecs binaries
  3. Centralizes what should be decentralized

I personally think we should remove this list in favor of each individual crate calling ambiguities.ignore("CRATE_NAME_HERE") if we agree that ambiguities should be ignored for a given crate.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clever. I think this is the best path forward here.

Copy link
Contributor Author

@afonsolage afonsolage Sep 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair points, but since the ignored crates list are stored in a resource, all crates should have a startup system which will add their crate names to it?

Also, we should add this to all bevy internal crates or only the ones which already have ambiguities reported?

And lastly, is on the scope of this PR add this info on all needed crates (I can do it, just wanna organize)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair points, but since the ignored crates list are stored in a resource, all crates should have a startup system which will add their crate names to it?

I think it should be a part of Plugin init (prior to the schedule running).

Also, we should add this to all bevy internal crates or only the ones which already have ambiguities reported?

Yeah I think this is the right call. It encourages us to be more precise about suppressing these. It also cuts down on the amount of work we're doing at startup.

And lastly, is on the scope of this PR add this info on all needed crates (I can do it, just wanna organize)?

Whatever works best for you!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I've added the ignore call on following plugins:

  • bevy_audio
  • bevy_render
  • bevy_sprite
  • bevy_text
  • bevy_ui
  • bevy_transform
  • bevy_winit

The only "trick" part is if users add the ReportExecutionOrderAmbiguities after adding DefaultPlugins or so, the default ignored crates will be overwritten. I've added this on docs and on example, informing users that if they need to customize the resource, it must be done before the plugins

@afonsolage
Copy link
Contributor Author

Maybe I'll need that help again with the spelling 😃

@afonsolage
Copy link
Contributor Author

What to do when the CI fails with a code that I haven't touched?

error: field is never read: `message`
  --> crates/bevy_ecs/examples/events.rs:46:5
   |
46 |     pub message: String,
   |     ^^^^^^^^^^^^^^^^^^^
   |
   = note: `-D dead-code` implied by `-D warnings`

error: field is never read: `random_value`
  --> crates/bevy_ecs/examples/events.rs:47:5
   |
47 |     pub random_value: f32,
   |     ^^^^^^^^^^^^^^^^^^^^^

error: could not compile `bevy_ecs` due to 2 previous errors
warning: build failed, waiting for other jobs to finish...
error: build failed
Error: Process completed with exit code 101.

@MinerSebas
Copy link
Contributor

That was already fixed by #2811 and can thus be ignored.

@alice-i-cecile alice-i-cecile added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Dec 15, 2021
@alice-i-cecile alice-i-cecile added M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide and removed S-Needs-Migration-Guide labels Jan 20, 2022
@@ -90,6 +90,12 @@ impl IntoSystemDescriptor<()> for ExclusiveSystemCoerced {
}
}

pub enum AmbiguityDetection {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have a default value.

@alice-i-cecile
Copy link
Member

bors try

@bors
Copy link
Contributor

bors bot commented Mar 3, 2022

try

Merge conflict.

@alice-i-cecile
Copy link
Member

@afonsolage; I'd like to try and get this merged early next week. Are you able to fix up the few remaining issues and makes sure CI passes?

Copy link
Contributor

@maniwani maniwani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Report ambiguities where all pairwise combinations of systems conflict on the same data as a group.

This change (as implemented) makes things less clear (increasingly so with larger stages).

Comment on lines +572 to +591
for (index_a, index_b, components) in ambiguities.drain(..) {
let systems = ambiguities_map.entry(components).or_default();
if !systems.contains(&index_a) {
systems.push(index_a);
}
if !systems.contains(&index_b) {
systems.push(index_b);
}
}

for (idx, (conflicts, systems_indices)) in ambiguities_map.drain().enumerate() {
let system_names = systems_indices
.iter()
.map(|index| systems[*index].name())
.collect::<Vec<_>>();

let system_components = conflicts
.iter()
.map(|id| world.components().get_info(*id).unwrap().name())
.collect::<Vec<_>>();
Copy link
Contributor

@maniwani maniwani Mar 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While it makes for fewer warning messages, "all systems that have a conflict involving T" is not the same as "all systems that conflict on T".

Reporting this way will make it harder to parse which systems actually conflict (which was obvious before), since not all systems being grouped together are actually running at the same time.

I think you maybe intended for "all systems that conflict on T at the same time" but I don't think that's easy to solve.

(edit: I also prefer how we currently report pairs with all their conflicts.)

@alice-i-cecile
Copy link
Member

Closing in favor of the rebased #4299, which builds on this work. @afonsolage: thank you so, so much for this contribution, I'm very excited to see it in the engine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Simple improvements to ambiguity reporting clarity
7 participants