Skip to content

Commit 4ac2a63

Browse files
Remove all existing system order ambiguities in DefaultPlugins (#15031)
# Objective As discussed in #7386, system order ambiguities within `DefaultPlugins` are a source of bugs in the engine and badly pollute diagnostic output for users. We should eliminate them! This PR is an alternative to #15027: with all external ambiguities silenced, this should be much less prone to merge conflicts and the test output should be much easier for authors to understand. Note that system order ambiguities are still permitted in the `RenderApp`: these need a bit of thought in terms of how to test them, and will be fairly involved to fix. While these aren't *good*, they'll generally only cause graphical bugs, not logic ones. ## Solution All remaining system order ambiguities have been resolved. Review this PR commit-by-commit to see how each of these problems were fixed. ## Testing `cargo run --example ambiguity_detection` passes with no panics or logging!
1 parent c620eb7 commit 4ac2a63

File tree

13 files changed

+73
-23
lines changed

13 files changed

+73
-23
lines changed

crates/bevy_animation/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1234,7 +1234,7 @@ impl Plugin for AnimationPlugin {
12341234
(
12351235
advance_transitions,
12361236
advance_animations,
1237-
animate_targets,
1237+
animate_targets.after(bevy_render::mesh::morph::inherit_weights),
12381238
expire_completed_transitions,
12391239
)
12401240
.chain()

crates/bevy_app/src/terminal_ctrl_c_handler.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ impl TerminalCtrlCHandlerPlugin {
4848
}
4949

5050
/// Sends a [`AppExit`] event when the user presses `Ctrl+C` on the terminal.
51-
fn exit_on_flag(mut events: EventWriter<AppExit>) {
51+
pub fn exit_on_flag(mut events: EventWriter<AppExit>) {
5252
if SHOULD_EXIT.load(Ordering::Relaxed) {
5353
events.send(AppExit::from_code(130));
5454
}

crates/bevy_asset/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,11 @@ impl Plugin for AssetPlugin {
222222
.init_asset::<()>()
223223
.add_event::<UntypedAssetLoadFailedEvent>()
224224
.configure_sets(PreUpdate, TrackAssets.after(handle_internal_asset_events))
225-
.add_systems(PreUpdate, handle_internal_asset_events)
225+
// `handle_internal_asset_events` requires the use of `&mut World`,
226+
// and as a result has ambiguous system ordering with all other systems in `PreUpdate`.
227+
// This is virtually never a real problem: asset loading is async and so anything that interacts directly with it
228+
// needs to be robust to stochastic delays anyways.
229+
.add_systems(PreUpdate, handle_internal_asset_events.ambiguous_with_all())
226230
.register_type::<AssetPath>();
227231
}
228232
}

crates/bevy_dev_tools/src/ci_testing/mod.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ mod systems;
66
pub use self::config::*;
77

88
use bevy_app::prelude::*;
9-
use bevy_ecs::schedule::IntoSystemConfigs;
9+
use bevy_ecs::prelude::*;
1010
use bevy_render::view::screenshot::trigger_screenshots;
1111
use bevy_time::TimeUpdateStrategy;
1212
use std::time::Duration;
@@ -54,7 +54,19 @@ impl Plugin for CiTestingPlugin {
5454
Update,
5555
systems::send_events
5656
.before(trigger_screenshots)
57-
.before(bevy_window::close_when_requested),
57+
.before(bevy_window::close_when_requested)
58+
.in_set(SendEvents),
5859
);
60+
61+
// The offending system does not exist in the wasm32 target.
62+
// As a result, we must conditionally order the two systems using a system set.
63+
#[cfg(not(target_arch = "wasm32"))]
64+
app.configure_sets(
65+
Update,
66+
SendEvents.before(bevy_app::TerminalCtrlCHandlerPlugin::exit_on_flag),
67+
);
5968
}
6069
}
70+
71+
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
72+
struct SendEvents;

crates/bevy_gizmos/src/aabb.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ impl Plugin for AabbGizmoPlugin {
4040
config.config::<AabbGizmoConfigGroup>().1.draw_all
4141
}),
4242
)
43+
.after(bevy_render::view::VisibilitySystems::CalculateBounds)
4344
.after(TransformSystem::TransformPropagate),
4445
);
4546
}

crates/bevy_gizmos/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ impl AppGizmoBuilder for App {
242242
handles.list.insert(TypeId::of::<Config>(), None);
243243
handles.strip.insert(TypeId::of::<Config>(), None);
244244

245+
// These handles are safe to mutate in any order
246+
self.allow_ambiguous_resource::<LineGizmoHandles>();
247+
245248
self.init_resource::<GizmoStorage<Config, ()>>()
246249
.init_resource::<GizmoStorage<Config, Fixed>>()
247250
.init_resource::<GizmoStorage<Config, Swap<Fixed>>>()

crates/bevy_internal/src/default_plugins.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ impl Plugin for IgnoreAmbiguitiesPlugin {
9090
bevy_animation::advance_animations,
9191
bevy_ui::ui_layout_system,
9292
);
93+
app.ignore_ambiguity(
94+
bevy_app::PostUpdate,
95+
bevy_animation::animate_targets,
96+
bevy_ui::ui_layout_system,
97+
);
9398
}
9499
}
95100
}

crates/bevy_pbr/src/lib.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,10 +340,22 @@ impl Plugin for PbrPlugin {
340340
)
341341
.chain(),
342342
)
343+
.configure_sets(
344+
PostUpdate,
345+
SimulationLightSystems::UpdateDirectionalLightCascades
346+
.ambiguous_with(SimulationLightSystems::UpdateDirectionalLightCascades),
347+
)
348+
.configure_sets(
349+
PostUpdate,
350+
SimulationLightSystems::CheckLightVisibility
351+
.ambiguous_with(SimulationLightSystems::CheckLightVisibility),
352+
)
343353
.add_systems(
344354
PostUpdate,
345355
(
346-
add_clusters.in_set(SimulationLightSystems::AddClusters),
356+
add_clusters
357+
.in_set(SimulationLightSystems::AddClusters)
358+
.after(CameraUpdateSystem),
347359
assign_objects_to_clusters
348360
.in_set(SimulationLightSystems::AssignLightsToClusters)
349361
.after(TransformSystem::TransformPropagate)

crates/bevy_pbr/src/light/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,12 +496,19 @@ pub enum ShadowFilteringMethod {
496496
Temporal,
497497
}
498498

499+
/// System sets used to run light-related systems.
499500
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
500501
pub enum SimulationLightSystems {
501502
AddClusters,
502503
AssignLightsToClusters,
504+
/// System order ambiguities between systems in this set are ignored:
505+
/// each [`build_directional_light_cascades`] system is independent of the others,
506+
/// and should operate on distinct sets of entities.
503507
UpdateDirectionalLightCascades,
504508
UpdateLightFrusta,
509+
/// System order ambiguities between systems in this set are ignored:
510+
/// the order of systems within this set is irrelevant, as the various visibility-checking systesms
511+
/// assumes that their operations are irreversible during the frame.
505512
CheckLightVisibility,
506513
}
507514

crates/bevy_picking/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,6 @@ impl Plugin for PickingPlugin {
225225
First,
226226
(PickSet::Input, PickSet::PostInput)
227227
.after(bevy_time::TimeSystem)
228-
.ambiguous_with(bevy_asset::handle_internal_asset_events)
229228
.after(bevy_ecs::event::EventUpdates)
230229
.chain(),
231230
)
@@ -239,7 +238,6 @@ impl Plugin for PickingPlugin {
239238
// Eventually events will need to be dispatched here
240239
PickSet::Last,
241240
)
242-
.ambiguous_with(bevy_asset::handle_internal_asset_events)
243241
.chain(),
244242
)
245243
.register_type::<pointer::PointerId>()

crates/bevy_render/src/view/visibility/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,11 @@ pub enum VisibilitySystems {
278278
/// [`hierarchy`](bevy_hierarchy).
279279
VisibilityPropagate,
280280
/// Label for the [`check_visibility`] system updating [`ViewVisibility`]
281-
/// of each entity and the [`VisibleEntities`] of each view.
281+
/// of each entity and the [`VisibleEntities`] of each view.\
282+
///
283+
/// System order ambiguities between systems in this set are ignored:
284+
/// the order of systems within this set is irrelevant, as [`check_visibility`]
285+
/// assumes that its operations are irreversible during the frame.
282286
CheckVisibility,
283287
}
284288

@@ -294,6 +298,7 @@ impl Plugin for VisibilityPlugin {
294298
.before(CheckVisibility)
295299
.after(TransformSystem::TransformPropagate),
296300
)
301+
.configure_sets(PostUpdate, CheckVisibility.ambiguous_with(CheckVisibility))
297302
.add_systems(
298303
PostUpdate,
299304
(

crates/bevy_ui/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,9 @@ impl Plugin for UiPlugin {
164164
update_target_camera_system.in_set(UiSystem::Prepare),
165165
ui_layout_system
166166
.in_set(UiSystem::Layout)
167-
.before(TransformSystem::TransformPropagate),
167+
.before(TransformSystem::TransformPropagate)
168+
// Text and Text2D operate on disjoint sets of entities
169+
.ambiguous_with(bevy_text::update_text2d_layout),
168170
ui_stack_system
169171
.in_set(UiSystem::Stack)
170172
// the systems don't care about stack index
@@ -226,7 +228,8 @@ fn build_text_interop(app: &mut App) {
226228
.in_set(UiSystem::PostLayout)
227229
.after(bevy_text::remove_dropped_font_atlas_sets)
228230
// Text2d and bevy_ui text are entirely on separate entities
229-
.ambiguous_with(bevy_text::update_text2d_layout),
231+
.ambiguous_with(bevy_text::update_text2d_layout)
232+
.ambiguous_with(bevy_text::calculate_bounds_text2d),
230233
),
231234
);
232235

tests/ecs/ambiguity_detection.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,22 @@ use bevy::{
99
prelude::*,
1010
utils::HashMap,
1111
};
12-
use bevy_render::{pipelined_rendering::RenderExtractApp, RenderApp};
12+
use bevy_render::pipelined_rendering::RenderExtractApp;
1313

1414
fn main() {
1515
let mut app = App::new();
1616
app.add_plugins(DefaultPlugins);
1717

18-
let sub_app = app.main_mut();
19-
configure_ambiguity_detection(sub_app);
20-
let sub_app = app.sub_app_mut(RenderApp);
21-
configure_ambiguity_detection(sub_app);
22-
let sub_app = app.sub_app_mut(RenderExtractApp);
23-
configure_ambiguity_detection(sub_app);
18+
let main_app = app.main_mut();
19+
configure_ambiguity_detection(main_app);
20+
let render_extract_app = app.sub_app_mut(RenderExtractApp);
21+
configure_ambiguity_detection(render_extract_app);
22+
23+
// Ambiguities in the RenderApp are currently allowed.
24+
// Eventually, we should forbid these: see https://github.com/bevyengine/bevy/issues/7386
25+
// Uncomment the lines below to show the current ambiguities in the RenderApp.
26+
// let sub_app = app.sub_app_mut(bevy_render::RenderApp);
27+
// configure_ambiguity_detection(sub_app);
2428

2529
app.finish();
2630
app.cleanup();
@@ -29,11 +33,7 @@ fn main() {
2933
let main_app_ambiguities = count_ambiguities(app.main());
3034
assert_eq!(
3135
main_app_ambiguities.total(),
32-
// This number *should* be zero.
33-
// Over time, we are working to reduce the number: your PR should not increase it.
34-
// If you decrease this by fixing an ambiguity, reduce the number to prevent regressions.
35-
// See https://github.com/bevyengine/bevy/issues/7386 for progress.
36-
44,
36+
0,
3737
"Main app has unexpected ambiguities among the following schedules: \n{:#?}.",
3838
main_app_ambiguities,
3939
);

0 commit comments

Comments
 (0)