diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a37d11ddf9..526ac4c401 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -95,7 +95,7 @@ jobs: - name: CI job # To run the tests one item at a time for troubleshooting, use # cargo --quiet test --lib -- --list | sed 's/: test$//' | MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-disable-weak-memory-emulation" xargs -n1 cargo miri test -p bevy_ecs --lib -- --exact - run: cargo miri test -p bevy_ecs + run: cargo miri test -p bevy_ecs --features bevy_utils/debug env: # -Zrandomize-layout makes sure we dont rely on the layout of anything that might change RUSTFLAGS: -Zrandomize-layout diff --git a/Cargo.toml b/Cargo.toml index ed2b369f5f..d2e5b92258 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -165,6 +165,7 @@ default = [ "vorbis", "webgl2", "x11", + "debug", ] # Recommended defaults for no_std applications @@ -507,7 +508,10 @@ file_watcher = ["bevy_internal/file_watcher"] embedded_watcher = ["bevy_internal/embedded_watcher"] # Enable stepping-based debugging of Bevy systems -bevy_debug_stepping = ["bevy_internal/bevy_debug_stepping"] +bevy_debug_stepping = [ + "bevy_internal/bevy_debug_stepping", + "bevy_internal/debug", +] # Enables the meshlet renderer for dense high-poly scenes (experimental) meshlet = ["bevy_internal/meshlet"] @@ -551,6 +555,9 @@ web = ["bevy_internal/web"] # Enable hotpatching of Bevy systems hotpatching = ["bevy_internal/hotpatching"] +# Enable collecting debug information about systems and components to help with diagnostics +debug = ["bevy_internal/debug"] + [dependencies] bevy_internal = { path = "crates/bevy_internal", version = "0.16.0-dev", default-features = false } tracing = { version = "0.1", default-features = false, optional = true } @@ -2098,6 +2105,7 @@ wasm = false name = "dynamic" path = "examples/ecs/dynamic.rs" doc-scrape-examples = true +required-features = ["debug"] [package.metadata.example.dynamic] name = "Dynamic ECS" diff --git a/crates/bevy_anti_aliasing/src/smaa/mod.rs b/crates/bevy_anti_aliasing/src/smaa/mod.rs index 0947abc5f8..bb082c5a01 100644 --- a/crates/bevy_anti_aliasing/src/smaa/mod.rs +++ b/crates/bevy_anti_aliasing/src/smaa/mod.rs @@ -847,7 +847,7 @@ impl ViewNode for SmaaNode { view_smaa_uniform_offset, smaa_textures, view_smaa_bind_groups, - ): QueryItem<'w, Self::ViewQuery>, + ): QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { let pipeline_cache = world.resource::(); diff --git a/crates/bevy_asset/macros/src/lib.rs b/crates/bevy_asset/macros/src/lib.rs index 443bd09ab9..a7ea87b752 100644 --- a/crates/bevy_asset/macros/src/lib.rs +++ b/crates/bevy_asset/macros/src/lib.rs @@ -1,6 +1,7 @@ -#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")] #![cfg_attr(docsrs, feature(doc_auto_cfg))] +//! Macros for deriving asset traits. + use bevy_macro_utils::BevyManifest; use proc_macro::{Span, TokenStream}; use quote::{format_ident, quote}; @@ -12,6 +13,7 @@ pub(crate) fn bevy_asset_path() -> Path { const DEPENDENCY_ATTRIBUTE: &str = "dependency"; +/// Implement the `Asset` trait. #[proc_macro_derive(Asset, attributes(dependency))] pub fn derive_asset(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); @@ -30,6 +32,7 @@ pub fn derive_asset(input: TokenStream) -> TokenStream { }) } +/// Implement the `VisitAssetDependencies` trait. #[proc_macro_derive(VisitAssetDependencies, attributes(dependency))] pub fn derive_asset_dependency_visitor(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); diff --git a/crates/bevy_asset/src/asset_changed.rs b/crates/bevy_asset/src/asset_changed.rs index d314fa3fd6..b43a8625e7 100644 --- a/crates/bevy_asset/src/asset_changed.rs +++ b/crates/bevy_asset/src/asset_changed.rs @@ -158,9 +158,9 @@ unsafe impl WorldQuery for AssetChanged { fetch } - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - state: &Self::State, + state: &'s Self::State, last_run: Tick, this_run: Tick, ) -> Self::Fetch<'w> { @@ -201,9 +201,9 @@ unsafe impl WorldQuery for AssetChanged { const IS_DENSE: bool = <&A>::IS_DENSE; - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( fetch: &mut Self::Fetch<'w>, - state: &Self::State, + state: &'s Self::State, archetype: &'w Archetype, table: &'w Table, ) { @@ -215,7 +215,11 @@ unsafe impl WorldQuery for AssetChanged { } } - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) { + unsafe fn set_table<'w, 's>( + fetch: &mut Self::Fetch<'w>, + state: &Self::State, + table: &'w Table, + ) { if let Some(inner) = &mut fetch.inner { // SAFETY: We delegate to the inner `set_table` for `A` unsafe { @@ -265,6 +269,7 @@ unsafe impl QueryFilter for AssetChanged { #[inline] unsafe fn filter_fetch( + state: &Self::State, fetch: &mut Self::Fetch<'_>, entity: Entity, table_row: TableRow, @@ -272,7 +277,7 @@ unsafe impl QueryFilter for AssetChanged { fetch.inner.as_mut().is_some_and(|inner| { // SAFETY: We delegate to the inner `fetch` for `A` unsafe { - let handle = <&A>::fetch(inner, entity, table_row); + let handle = <&A>::fetch(&state.asset_id, inner, entity, table_row); fetch.check.has_changed(handle) } }) diff --git a/crates/bevy_asset/src/server/mod.rs b/crates/bevy_asset/src/server/mod.rs index 2b3898cd54..e120888616 100644 --- a/crates/bevy_asset/src/server/mod.rs +++ b/crates/bevy_asset/src/server/mod.rs @@ -1953,6 +1953,14 @@ impl AssetLoaderError { pub fn path(&self) -> &AssetPath<'static> { &self.path } + + /// The error the loader reported when attempting to load the asset. + /// + /// If you know the type of the error the asset loader returned, you can use + /// [`BevyError::downcast_ref()`] to get it. + pub fn error(&self) -> &BevyError { + &self.error + } } /// An error that occurs while resolving an asset added by `add_async`. diff --git a/crates/bevy_core_pipeline/src/bloom/mod.rs b/crates/bevy_core_pipeline/src/bloom/mod.rs index 10ffdf9c63..65e51c8472 100644 --- a/crates/bevy_core_pipeline/src/bloom/mod.rs +++ b/crates/bevy_core_pipeline/src/bloom/mod.rs @@ -121,7 +121,7 @@ impl ViewNode for BloomNode { bloom_settings, upsampling_pipeline_ids, downsampling_pipeline_ids, - ): QueryItem<'w, Self::ViewQuery>, + ): QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { if bloom_settings.intensity == 0.0 { diff --git a/crates/bevy_core_pipeline/src/bloom/settings.rs b/crates/bevy_core_pipeline/src/bloom/settings.rs index 195c2eb4c0..435ed037b5 100644 --- a/crates/bevy_core_pipeline/src/bloom/settings.rs +++ b/crates/bevy_core_pipeline/src/bloom/settings.rs @@ -227,7 +227,7 @@ impl ExtractComponent for Bloom { type QueryFilter = With; type Out = (Self, BloomUniforms); - fn extract_component((bloom, camera): QueryItem<'_, Self::QueryData>) -> Option { + fn extract_component((bloom, camera): QueryItem<'_, '_, Self::QueryData>) -> Option { match ( camera.physical_viewport_rect(), camera.physical_viewport_size(), diff --git a/crates/bevy_core_pipeline/src/core_2d/main_opaque_pass_2d_node.rs b/crates/bevy_core_pipeline/src/core_2d/main_opaque_pass_2d_node.rs index 60f355c115..e8cd0c65c6 100644 --- a/crates/bevy_core_pipeline/src/core_2d/main_opaque_pass_2d_node.rs +++ b/crates/bevy_core_pipeline/src/core_2d/main_opaque_pass_2d_node.rs @@ -31,7 +31,7 @@ impl ViewNode for MainOpaquePass2dNode { &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext<'w>, - (camera, view, target, depth): QueryItem<'w, Self::ViewQuery>, + (camera, view, target, depth): QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { let (Some(opaque_phases), Some(alpha_mask_phases)) = ( diff --git a/crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs b/crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs index 494d4d0f89..4054283a57 100644 --- a/crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs +++ b/crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs @@ -28,7 +28,7 @@ impl ViewNode for MainTransparentPass2dNode { &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext<'w>, - (camera, view, target, depth): bevy_ecs::query::QueryItem<'w, Self::ViewQuery>, + (camera, view, target, depth): bevy_ecs::query::QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { let Some(transparent_phases) = diff --git a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs index 3b1bc96c90..b19268ac1f 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs @@ -45,7 +45,7 @@ impl ViewNode for MainOpaquePass3dNode { skybox_pipeline, skybox_bind_group, view_uniform_offset, - ): QueryItem<'w, Self::ViewQuery>, + ): QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { let (Some(opaque_phases), Some(alpha_mask_phases)) = ( diff --git a/crates/bevy_core_pipeline/src/deferred/node.rs b/crates/bevy_core_pipeline/src/deferred/node.rs index ffac1eec6d..e786d2a222 100644 --- a/crates/bevy_core_pipeline/src/deferred/node.rs +++ b/crates/bevy_core_pipeline/src/deferred/node.rs @@ -36,7 +36,7 @@ impl ViewNode for EarlyDeferredGBufferPrepassNode { &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext<'w>, - view_query: QueryItem<'w, Self::ViewQuery>, + view_query: QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { run_deferred_prepass( @@ -74,7 +74,7 @@ impl ViewNode for LateDeferredGBufferPrepassNode { &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext<'w>, - view_query: QueryItem<'w, Self::ViewQuery>, + view_query: QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { let (_, _, _, _, occlusion_culling, no_indirect_drawing) = view_query; @@ -107,6 +107,7 @@ fn run_deferred_prepass<'w>( render_context: &mut RenderContext<'w>, (camera, extracted_view, view_depth_texture, view_prepass_textures, _, _): QueryItem< 'w, + '_, ::ViewQuery, >, is_late: bool, diff --git a/crates/bevy_core_pipeline/src/dof/mod.rs b/crates/bevy_core_pipeline/src/dof/mod.rs index 38f5e1e796..c27d81180d 100644 --- a/crates/bevy_core_pipeline/src/dof/mod.rs +++ b/crates/bevy_core_pipeline/src/dof/mod.rs @@ -352,7 +352,7 @@ impl ViewNode for DepthOfFieldNode { view_bind_group_layouts, depth_of_field_uniform_index, auxiliary_dof_texture, - ): QueryItem<'w, Self::ViewQuery>, + ): QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { let pipeline_cache = world.resource::(); diff --git a/crates/bevy_core_pipeline/src/msaa_writeback.rs b/crates/bevy_core_pipeline/src/msaa_writeback.rs index 8dc51e4ed5..5f82e10599 100644 --- a/crates/bevy_core_pipeline/src/msaa_writeback.rs +++ b/crates/bevy_core_pipeline/src/msaa_writeback.rs @@ -61,7 +61,7 @@ impl ViewNode for MsaaWritebackNode { &self, _graph: &mut RenderGraphContext, render_context: &mut RenderContext<'w>, - (target, blit_pipeline_id, msaa): QueryItem<'w, Self::ViewQuery>, + (target, blit_pipeline_id, msaa): QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { if *msaa == Msaa::Off { diff --git a/crates/bevy_core_pipeline/src/post_process/mod.rs b/crates/bevy_core_pipeline/src/post_process/mod.rs index 1ab03c5dfa..0f188d1d73 100644 --- a/crates/bevy_core_pipeline/src/post_process/mod.rs +++ b/crates/bevy_core_pipeline/src/post_process/mod.rs @@ -352,7 +352,7 @@ impl ViewNode for PostProcessingNode { &self, _: &mut RenderGraphContext, render_context: &mut RenderContext<'w>, - (view_target, pipeline_id, chromatic_aberration, post_processing_uniform_buffer_offsets): QueryItem<'w, Self::ViewQuery>, + (view_target, pipeline_id, chromatic_aberration, post_processing_uniform_buffer_offsets): QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { let pipeline_cache = world.resource::(); @@ -485,7 +485,7 @@ impl ExtractComponent for ChromaticAberration { type Out = ChromaticAberration; fn extract_component( - chromatic_aberration: QueryItem<'_, Self::QueryData>, + chromatic_aberration: QueryItem<'_, '_, Self::QueryData>, ) -> Option { // Skip the postprocessing phase entirely if the intensity is zero. if chromatic_aberration.intensity > 0.0 { diff --git a/crates/bevy_core_pipeline/src/prepass/node.rs b/crates/bevy_core_pipeline/src/prepass/node.rs index 04cc1890b0..500cc0a42b 100644 --- a/crates/bevy_core_pipeline/src/prepass/node.rs +++ b/crates/bevy_core_pipeline/src/prepass/node.rs @@ -36,7 +36,7 @@ impl ViewNode for EarlyPrepassNode { &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext<'w>, - view_query: QueryItem<'w, Self::ViewQuery>, + view_query: QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { run_prepass(graph, render_context, view_query, world, "early prepass") @@ -73,7 +73,7 @@ impl ViewNode for LatePrepassNode { &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext<'w>, - query: QueryItem<'w, Self::ViewQuery>, + query: QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { // We only need a late prepass if we have occlusion culling and indirect @@ -112,7 +112,7 @@ fn run_prepass<'w>( _, _, has_deferred, - ): QueryItem<'w, ::ViewQuery>, + ): QueryItem<'w, '_, ::ViewQuery>, world: &'w World, label: &'static str, ) -> Result<(), NodeRunError> { diff --git a/crates/bevy_core_pipeline/src/skybox/mod.rs b/crates/bevy_core_pipeline/src/skybox/mod.rs index cb75df2053..51c6934ece 100644 --- a/crates/bevy_core_pipeline/src/skybox/mod.rs +++ b/crates/bevy_core_pipeline/src/skybox/mod.rs @@ -113,7 +113,9 @@ impl ExtractComponent for Skybox { type QueryFilter = (); type Out = (Self, SkyboxUniforms); - fn extract_component((skybox, exposure): QueryItem<'_, Self::QueryData>) -> Option { + fn extract_component( + (skybox, exposure): QueryItem<'_, '_, Self::QueryData>, + ) -> Option { let exposure = exposure .map(Exposure::exposure) .unwrap_or_else(|| Exposure::default().exposure()); @@ -123,7 +125,7 @@ impl ExtractComponent for Skybox { SkyboxUniforms { brightness: skybox.brightness * exposure, transform: Transform::from_rotation(skybox.rotation) - .compute_matrix() + .to_matrix() .inverse(), #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] _wasm_padding_8b: 0, diff --git a/crates/bevy_core_widgets/src/core_slider.rs b/crates/bevy_core_widgets/src/core_slider.rs index 63a606be78..ecd6d52fbe 100644 --- a/crates/bevy_core_widgets/src/core_slider.rs +++ b/crates/bevy_core_widgets/src/core_slider.rs @@ -211,6 +211,7 @@ pub(crate) fn slider_on_pointer_down( focus: Option>, focus_visible: Option>, mut commands: Commands, + ui_scale: Res, ) { if q_thumb.contains(trigger.target()) { // Thumb click, stop propagation to prevent track click. @@ -255,7 +256,7 @@ pub(crate) fn slider_on_pointer_down( // Detect track click. let local_pos = transform.try_inverse().unwrap().transform_point2( - trigger.event().pointer_location.position * node_target.scale_factor(), + trigger.event().pointer_location.position * node_target.scale_factor() / ui_scale.0, ); let track_width = node.size().x - thumb_size; // Avoid division by zero @@ -394,6 +395,10 @@ fn slider_on_key_input( trigger.propagate(false); if let Some(on_change) = slider.on_change { commands.run_system_with(on_change, new_value); + } else { + commands + .entity(trigger.target()) + .insert(SliderValue(new_value)); } } } @@ -456,7 +461,7 @@ pub(crate) fn slider_on_insert_step(trigger: On, mut world: /// commands.trigger_targets(SetSliderValue::Relative(-0.25), slider); /// } /// ``` -#[derive(Event, EntityEvent)] +#[derive(Event, EntityEvent, Clone)] pub enum SetSliderValue { /// Set the slider value to a specific value. Absolute(f32), diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index 27498f58bc..c2c663b7d7 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -35,7 +35,7 @@ backtrace = ["std"] ## Enables `tracing` integration, allowing spans and other metrics to be reported ## through that framework. -trace = ["std", "dep:tracing"] +trace = ["std", "dep:tracing", "bevy_utils/debug"] ## Enables a more detailed set of traces which may be noisy if left on by default. detailed_trace = ["trace"] @@ -63,9 +63,9 @@ std = [ "bevy_reflect?/std", "bevy_tasks/std", "bevy_utils/parallel", + "bevy_utils/std", "bitflags/std", "concurrent-queue/std", - "disqualified/alloc", "fixedbitset/std", "indexmap/std", "serde?/std", @@ -98,7 +98,6 @@ bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-fea ] } bitflags = { version = "2.3", default-features = false } -disqualified = { version = "1.0", default-features = false } fixedbitset = { version = "0.5", default-features = false } serde = { version = "1", default-features = false, features = [ "alloc", diff --git a/crates/bevy_ecs/macros/src/query_data.rs b/crates/bevy_ecs/macros/src/query_data.rs index 4e4529e631..910d9ce3b6 100644 --- a/crates/bevy_ecs/macros/src/query_data.rs +++ b/crates/bevy_ecs/macros/src/query_data.rs @@ -74,12 +74,23 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream { let user_generics = ast.generics.clone(); let (user_impl_generics, user_ty_generics, user_where_clauses) = user_generics.split_for_impl(); let user_generics_with_world = { - let mut generics = ast.generics; + let mut generics = ast.generics.clone(); generics.params.insert(0, parse_quote!('__w)); generics }; let (user_impl_generics_with_world, user_ty_generics_with_world, user_where_clauses_with_world) = user_generics_with_world.split_for_impl(); + let user_generics_with_world_and_state = { + let mut generics = ast.generics; + generics.params.insert(0, parse_quote!('__w)); + generics.params.insert(0, parse_quote!('__s)); + generics + }; + let ( + user_impl_generics_with_world_and_state, + user_ty_generics_with_world_and_state, + user_where_clauses_with_world_and_state, + ) = user_generics_with_world_and_state.split_for_impl(); let struct_name = ast.ident; let read_only_struct_name = if attributes.is_mutable { @@ -164,13 +175,13 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream { &visibility, &item_struct_name, &field_types, - &user_impl_generics_with_world, + &user_impl_generics_with_world_and_state, &field_attrs, &field_visibilities, &field_idents, &user_ty_generics, - &user_ty_generics_with_world, - user_where_clauses_with_world, + &user_ty_generics_with_world_and_state, + user_where_clauses_with_world_and_state, ); let mutable_world_query_impl = world_query_impl( &path, @@ -199,13 +210,13 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream { &visibility, &read_only_item_struct_name, &read_only_field_types, - &user_impl_generics_with_world, + &user_impl_generics_with_world_and_state, &field_attrs, &field_visibilities, &field_idents, &user_ty_generics, - &user_ty_generics_with_world, - user_where_clauses_with_world, + &user_ty_generics_with_world_and_state, + user_where_clauses_with_world_and_state, ); let readonly_world_query_impl = world_query_impl( &path, @@ -256,11 +267,11 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream { for #read_only_struct_name #user_ty_generics #user_where_clauses { const IS_READ_ONLY: bool = true; type ReadOnly = #read_only_struct_name #user_ty_generics; - type Item<'__w> = #read_only_item_struct_name #user_ty_generics_with_world; + type Item<'__w, '__s> = #read_only_item_struct_name #user_ty_generics_with_world_and_state; - fn shrink<'__wlong: '__wshort, '__wshort>( - item: Self::Item<'__wlong> - ) -> Self::Item<'__wshort> { + fn shrink<'__wlong: '__wshort, '__wshort, '__s>( + item: Self::Item<'__wlong, '__s> + ) -> Self::Item<'__wshort, '__s> { #read_only_item_struct_name { #( #field_idents: <#read_only_field_types>::shrink(item.#field_idents), @@ -278,13 +289,26 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream { /// SAFETY: we call `fetch` for each member that implements `Fetch`. #[inline(always)] - unsafe fn fetch<'__w>( + unsafe fn fetch<'__w, '__s>( + _state: &'__s Self::State, _fetch: &mut ::Fetch<'__w>, _entity: #path::entity::Entity, _table_row: #path::storage::TableRow, - ) -> Self::Item<'__w> { + ) -> Self::Item<'__w, '__s> { Self::Item { - #(#field_idents: <#read_only_field_types>::fetch(&mut _fetch.#named_field_idents, _entity, _table_row),)* + #(#field_idents: <#read_only_field_types>::fetch(&_state.#named_field_idents, &mut _fetch.#named_field_idents, _entity, _table_row),)* + } + } + } + + impl #user_impl_generics #path::query::ReleaseStateQueryData + for #read_only_struct_name #user_ty_generics #user_where_clauses + // Make these HRTBs with an unused lifetime parameter to allow trivial constraints + // See https://github.com/rust-lang/rust/issues/48214 + where #(for<'__a> #field_types: #path::query::QueryData,)* { + fn release_state<'__w>(_item: Self::Item<'__w, '_>) -> Self::Item<'__w, 'static> { + Self::Item { + #(#field_idents: <#read_only_field_types>::release_state(_item.#field_idents),)* } } } @@ -301,11 +325,11 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream { for #struct_name #user_ty_generics #user_where_clauses { const IS_READ_ONLY: bool = #is_read_only; type ReadOnly = #read_only_struct_name #user_ty_generics; - type Item<'__w> = #item_struct_name #user_ty_generics_with_world; + type Item<'__w, '__s> = #item_struct_name #user_ty_generics_with_world_and_state; - fn shrink<'__wlong: '__wshort, '__wshort>( - item: Self::Item<'__wlong> - ) -> Self::Item<'__wshort> { + fn shrink<'__wlong: '__wshort, '__wshort, '__s>( + item: Self::Item<'__wlong, '__s> + ) -> Self::Item<'__wshort, '__s> { #item_struct_name { #( #field_idents: <#field_types>::shrink(item.#field_idents), @@ -323,13 +347,26 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream { /// SAFETY: we call `fetch` for each member that implements `Fetch`. #[inline(always)] - unsafe fn fetch<'__w>( + unsafe fn fetch<'__w, '__s>( + _state: &'__s Self::State, _fetch: &mut ::Fetch<'__w>, _entity: #path::entity::Entity, _table_row: #path::storage::TableRow, - ) -> Self::Item<'__w> { + ) -> Self::Item<'__w, '__s> { Self::Item { - #(#field_idents: <#field_types>::fetch(&mut _fetch.#named_field_idents, _entity, _table_row),)* + #(#field_idents: <#field_types>::fetch(&_state.#named_field_idents, &mut _fetch.#named_field_idents, _entity, _table_row),)* + } + } + } + + impl #user_impl_generics #path::query::ReleaseStateQueryData + for #struct_name #user_ty_generics #user_where_clauses + // Make these HRTBs with an unused lifetime parameter to allow trivial constraints + // See https://github.com/rust-lang/rust/issues/48214 + where #(for<'__a> #field_types: #path::query::ReleaseStateQueryData,)* { + fn release_state<'__w>(_item: Self::Item<'__w, '_>) -> Self::Item<'__w, 'static> { + Self::Item { + #(#field_idents: <#field_types>::release_state(_item.#field_idents),)* } } } diff --git a/crates/bevy_ecs/macros/src/query_filter.rs b/crates/bevy_ecs/macros/src/query_filter.rs index c7ddb9cc83..5ae2d2325f 100644 --- a/crates/bevy_ecs/macros/src/query_filter.rs +++ b/crates/bevy_ecs/macros/src/query_filter.rs @@ -102,11 +102,12 @@ pub fn derive_query_filter_impl(input: TokenStream) -> TokenStream { #[allow(unused_variables)] #[inline(always)] unsafe fn filter_fetch<'__w>( + _state: &Self::State, _fetch: &mut ::Fetch<'__w>, _entity: #path::entity::Entity, _table_row: #path::storage::TableRow, ) -> bool { - true #(&& <#field_types>::filter_fetch(&mut _fetch.#named_field_idents, _entity, _table_row))* + true #(&& <#field_types>::filter_fetch(&_state.#named_field_idents, &mut _fetch.#named_field_idents, _entity, _table_row))* } } }; diff --git a/crates/bevy_ecs/macros/src/world_query.rs b/crates/bevy_ecs/macros/src/world_query.rs index 5c4c0bff01..5a7d164b80 100644 --- a/crates/bevy_ecs/macros/src/world_query.rs +++ b/crates/bevy_ecs/macros/src/world_query.rs @@ -10,13 +10,13 @@ pub(crate) fn item_struct( visibility: &Visibility, item_struct_name: &Ident, field_types: &Vec, - user_impl_generics_with_world: &ImplGenerics, + user_impl_generics_with_world_and_state: &ImplGenerics, field_attrs: &Vec>, field_visibilities: &Vec, field_idents: &Vec, user_ty_generics: &TypeGenerics, - user_ty_generics_with_world: &TypeGenerics, - user_where_clauses_with_world: Option<&WhereClause>, + user_ty_generics_with_world_and_state: &TypeGenerics, + user_where_clauses_with_world_and_state: Option<&WhereClause>, ) -> proc_macro2::TokenStream { let item_attrs = quote! { #[doc = concat!( @@ -33,20 +33,20 @@ pub(crate) fn item_struct( Fields::Named(_) => quote! { #derive_macro_call #item_attrs - #visibility struct #item_struct_name #user_impl_generics_with_world #user_where_clauses_with_world { - #(#(#field_attrs)* #field_visibilities #field_idents: <#field_types as #path::query::QueryData>::Item<'__w>,)* + #visibility struct #item_struct_name #user_impl_generics_with_world_and_state #user_where_clauses_with_world_and_state { + #(#(#field_attrs)* #field_visibilities #field_idents: <#field_types as #path::query::QueryData>::Item<'__w, '__s>,)* } }, Fields::Unnamed(_) => quote! { #derive_macro_call #item_attrs - #visibility struct #item_struct_name #user_impl_generics_with_world #user_where_clauses_with_world( - #( #field_visibilities <#field_types as #path::query::QueryData>::Item<'__w>, )* + #visibility struct #item_struct_name #user_impl_generics_with_world_and_state #user_where_clauses_with_world_and_state( + #( #field_visibilities <#field_types as #path::query::QueryData>::Item<'__w, '__s>, )* ); }, Fields::Unit => quote! { #item_attrs - #visibility type #item_struct_name #user_ty_generics_with_world = #struct_name #user_ty_generics; + #visibility type #item_struct_name #user_ty_generics_with_world_and_state = #struct_name #user_ty_generics; }, } } @@ -79,7 +79,7 @@ pub(crate) fn world_query_impl( #[automatically_derived] #visibility struct #fetch_struct_name #user_impl_generics_with_world #user_where_clauses_with_world { #(#named_field_idents: <#field_types as #path::query::WorldQuery>::Fetch<'__w>,)* - #marker_name: &'__w (), + #marker_name: &'__w(), } impl #user_impl_generics_with_world Clone for #fetch_struct_name #user_ty_generics_with_world @@ -110,9 +110,9 @@ pub(crate) fn world_query_impl( } } - unsafe fn init_fetch<'__w>( + unsafe fn init_fetch<'__w, '__s>( _world: #path::world::unsafe_world_cell::UnsafeWorldCell<'__w>, - state: &Self::State, + state: &'__s Self::State, _last_run: #path::component::Tick, _this_run: #path::component::Tick, ) -> ::Fetch<'__w> { @@ -133,9 +133,9 @@ pub(crate) fn world_query_impl( /// SAFETY: we call `set_archetype` for each member that implements `Fetch` #[inline] - unsafe fn set_archetype<'__w>( + unsafe fn set_archetype<'__w, '__s>( _fetch: &mut ::Fetch<'__w>, - _state: &Self::State, + _state: &'__s Self::State, _archetype: &'__w #path::archetype::Archetype, _table: &'__w #path::storage::Table ) { @@ -144,9 +144,9 @@ pub(crate) fn world_query_impl( /// SAFETY: we call `set_table` for each member that implements `Fetch` #[inline] - unsafe fn set_table<'__w>( + unsafe fn set_table<'__w, '__s>( _fetch: &mut ::Fetch<'__w>, - _state: &Self::State, + _state: &'__s Self::State, _table: &'__w #path::storage::Table ) { #(<#field_types>::set_table(&mut _fetch.#named_field_idents, &_state.#named_field_idents, _table);)* diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 827331288d..b672a56483 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -550,10 +550,9 @@ impl BundleInfo { // SAFETY: the caller ensures component_id is valid. unsafe { components.get_info_unchecked(id).name() } }) - .collect::>() - .join(", "); + .collect::>(); - panic!("Bundle {bundle_type_name} has duplicate components: {names}"); + panic!("Bundle {bundle_type_name} has duplicate components: {names:?}"); } // handle explicit components diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 27b51ede6f..cfcde29ab2 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -24,7 +24,7 @@ use bevy_platform::{ use bevy_ptr::{OwningPtr, UnsafeCellDeref}; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; -use bevy_utils::TypeIdMap; +use bevy_utils::{prelude::DebugName, TypeIdMap}; use core::{ alloc::Layout, any::{Any, TypeId}, @@ -34,7 +34,6 @@ use core::{ mem::needs_drop, ops::{Deref, DerefMut}, }; -use disqualified::ShortName; use smallvec::SmallVec; use thiserror::Error; @@ -678,8 +677,8 @@ impl ComponentInfo { /// Returns the name of the current component. #[inline] - pub fn name(&self) -> &str { - &self.descriptor.name + pub fn name(&self) -> DebugName { + self.descriptor.name.clone() } /// Returns `true` if the current component is mutable. @@ -836,7 +835,7 @@ impl SparseSetIndex for ComponentId { /// A value describing a component or resource, which may or may not correspond to a Rust type. #[derive(Clone)] pub struct ComponentDescriptor { - name: Cow<'static, str>, + name: DebugName, // SAFETY: This must remain private. It must match the statically known StorageType of the // associated rust component type if one exists. storage_type: StorageType, @@ -882,7 +881,7 @@ impl ComponentDescriptor { /// Create a new `ComponentDescriptor` for the type `T`. pub fn new() -> Self { Self { - name: Cow::Borrowed(core::any::type_name::()), + name: DebugName::type_name::(), storage_type: T::STORAGE_TYPE, is_send_and_sync: true, type_id: Some(TypeId::of::()), @@ -907,7 +906,7 @@ impl ComponentDescriptor { clone_behavior: ComponentCloneBehavior, ) -> Self { Self { - name: name.into(), + name: name.into().into(), storage_type, is_send_and_sync: true, type_id: None, @@ -923,7 +922,7 @@ impl ComponentDescriptor { /// The [`StorageType`] for resources is always [`StorageType::Table`]. pub fn new_resource() -> Self { Self { - name: Cow::Borrowed(core::any::type_name::()), + name: DebugName::type_name::(), // PERF: `SparseStorage` may actually be a more // reasonable choice as `storage_type` for resources. storage_type: StorageType::Table, @@ -938,7 +937,7 @@ impl ComponentDescriptor { fn new_non_send(storage_type: StorageType) -> Self { Self { - name: Cow::Borrowed(core::any::type_name::()), + name: DebugName::type_name::(), storage_type, is_send_and_sync: false, type_id: Some(TypeId::of::()), @@ -964,8 +963,8 @@ impl ComponentDescriptor { /// Returns the name of the current component. #[inline] - pub fn name(&self) -> &str { - self.name.as_ref() + pub fn name(&self) -> DebugName { + self.name.clone() } /// Returns whether this component is mutable. @@ -1854,13 +1853,10 @@ impl Components { /// /// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value. #[inline] - pub fn get_name<'a>(&'a self, id: ComponentId) -> Option> { + pub fn get_name<'a>(&'a self, id: ComponentId) -> Option { self.components .get(id.0) - .and_then(|info| { - info.as_ref() - .map(|info| Cow::Borrowed(info.descriptor.name())) - }) + .and_then(|info| info.as_ref().map(|info| info.descriptor.name())) .or_else(|| { let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner); // first check components, then resources, then dynamic @@ -2813,13 +2809,13 @@ pub fn enforce_no_required_components_recursion( "Recursive required components detected: {}\nhelp: {}", recursion_check_stack .iter() - .map(|id| format!("{}", ShortName(&components.get_name(*id).unwrap()))) + .map(|id| format!("{}", components.get_name(*id).unwrap().shortname())) .collect::>() .join(" → "), if direct_recursion { format!( "Remove require({}).", - ShortName(&components.get_name(requiree).unwrap()) + components.get_name(requiree).unwrap().shortname() ) } else { "If this is intentional, consider merging the components.".into() diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 9983ac2e47..0dcdfe8d4f 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -10,6 +10,7 @@ use crate::{ use alloc::{borrow::ToOwned, boxed::Box, collections::VecDeque, vec::Vec}; use bevy_platform::collections::{HashMap, HashSet}; use bevy_ptr::{Ptr, PtrMut}; +use bevy_utils::prelude::DebugName; use bumpalo::Bump; use core::any::TypeId; @@ -172,7 +173,8 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { /// - `ComponentId` of component being written does not match expected `ComponentId`. pub fn write_target_component(&mut self, mut component: C) { C::map_entities(&mut component, &mut self.mapper); - let short_name = disqualified::ShortName::of::(); + let debug_name = DebugName::type_name::(); + let short_name = debug_name.shortname(); if self.target_component_written { panic!("Trying to write component '{short_name}' multiple times") } diff --git a/crates/bevy_ecs/src/error/command_handling.rs b/crates/bevy_ecs/src/error/command_handling.rs index bf2741d376..c303b76d17 100644 --- a/crates/bevy_ecs/src/error/command_handling.rs +++ b/crates/bevy_ecs/src/error/command_handling.rs @@ -1,4 +1,6 @@ -use core::{any::type_name, fmt}; +use core::fmt; + +use bevy_utils::prelude::DebugName; use crate::{ entity::Entity, @@ -31,7 +33,7 @@ where Err(err) => (error_handler)( err.into(), ErrorContext::Command { - name: type_name::().into(), + name: DebugName::type_name::(), }, ), } @@ -43,7 +45,7 @@ where Err(err) => world.default_error_handler()( err.into(), ErrorContext::Command { - name: type_name::().into(), + name: DebugName::type_name::(), }, ), } diff --git a/crates/bevy_ecs/src/error/handler.rs b/crates/bevy_ecs/src/error/handler.rs index c89408b250..85a5a13297 100644 --- a/crates/bevy_ecs/src/error/handler.rs +++ b/crates/bevy_ecs/src/error/handler.rs @@ -1,7 +1,7 @@ use core::fmt::Display; use crate::{component::Tick, error::BevyError, prelude::Resource}; -use alloc::borrow::Cow; +use bevy_utils::prelude::DebugName; use derive_more::derive::{Deref, DerefMut}; /// Context for a [`BevyError`] to aid in debugging. @@ -10,26 +10,26 @@ pub enum ErrorContext { /// The error occurred in a system. System { /// The name of the system that failed. - name: Cow<'static, str>, + name: DebugName, /// The last tick that the system was run. last_run: Tick, }, /// The error occurred in a run condition. RunCondition { /// The name of the run condition that failed. - name: Cow<'static, str>, + name: DebugName, /// The last tick that the run condition was evaluated. last_run: Tick, }, /// The error occurred in a command. Command { /// The name of the command that failed. - name: Cow<'static, str>, + name: DebugName, }, /// The error occurred in an observer. Observer { /// The name of the observer that failed. - name: Cow<'static, str>, + name: DebugName, /// The last tick that the observer was run. last_run: Tick, }, @@ -54,12 +54,12 @@ impl Display for ErrorContext { impl ErrorContext { /// The name of the ECS construct that failed. - pub fn name(&self) -> &str { + pub fn name(&self) -> DebugName { match self { Self::System { name, .. } | Self::Command { name, .. } | Self::Observer { name, .. } - | Self::RunCondition { name, .. } => name, + | Self::RunCondition { name, .. } => name.clone(), } } diff --git a/crates/bevy_ecs/src/hierarchy.rs b/crates/bevy_ecs/src/hierarchy.rs index 31c7b5e65a..d99e89b355 100644 --- a/crates/bevy_ecs/src/hierarchy.rs +++ b/crates/bevy_ecs/src/hierarchy.rs @@ -22,9 +22,9 @@ use alloc::{format, string::String, vec::Vec}; use bevy_reflect::std_traits::ReflectDefault; #[cfg(all(feature = "serialize", feature = "bevy_reflect"))] use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; +use bevy_utils::prelude::DebugName; use core::ops::Deref; use core::slice; -use disqualified::ShortName; use log::warn; /// Stores the parent entity of this child entity with this component. @@ -461,11 +461,12 @@ pub fn validate_parent_has_component( { // TODO: print name here once Name lives in bevy_ecs let name: Option = None; + let debug_name = DebugName::type_name::(); warn!( "warning[B0004]: {}{name} with the {ty_name} component has a parent without {ty_name}.\n\ This will cause inconsistent behaviors! See: https://bevy.org/learn/errors/b0004", caller.map(|c| format!("{c}: ")).unwrap_or_default(), - ty_name = ShortName::of::(), + ty_name = debug_name.shortname(), name = name.map_or_else( || format!("Entity {entity}"), |s| format!("The {s} entity") diff --git a/crates/bevy_ecs/src/lifecycle.rs b/crates/bevy_ecs/src/lifecycle.rs index be5765beee..e92c6cc7f9 100644 --- a/crates/bevy_ecs/src/lifecycle.rs +++ b/crates/bevy_ecs/src/lifecycle.rs @@ -328,7 +328,7 @@ pub const DESPAWN: ComponentId = ComponentId::new(4); /// Trigger emitted when a component is inserted onto an entity that does not already have that /// component. Runs before `Insert`. /// See [`crate::lifecycle::ComponentHooks::on_add`] for more information. -#[derive(Event, EntityEvent, Debug)] +#[derive(Event, EntityEvent, Debug, Clone)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))] #[doc(alias = "OnAdd")] @@ -337,7 +337,7 @@ pub struct Add; /// Trigger emitted when a component is inserted, regardless of whether or not the entity already /// had that component. Runs after `Add`, if it ran. /// See [`crate::lifecycle::ComponentHooks::on_insert`] for more information. -#[derive(Event, EntityEvent, Debug)] +#[derive(Event, EntityEvent, Debug, Clone)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))] #[doc(alias = "OnInsert")] @@ -348,7 +348,7 @@ pub struct Insert; /// /// Runs before the value is replaced, so you can still access the original component data. /// See [`crate::lifecycle::ComponentHooks::on_replace`] for more information. -#[derive(Event, EntityEvent, Debug)] +#[derive(Event, EntityEvent, Debug, Clone)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))] #[doc(alias = "OnReplace")] @@ -357,7 +357,7 @@ pub struct Replace; /// Trigger emitted when a component is removed from an entity, and runs before the component is /// removed, so you can still access the component data. /// See [`crate::lifecycle::ComponentHooks::on_remove`] for more information. -#[derive(Event, EntityEvent, Debug)] +#[derive(Event, EntityEvent, Debug, Clone)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))] #[doc(alias = "OnRemove")] @@ -365,7 +365,7 @@ pub struct Remove; /// Trigger emitted for each component on an entity when it is despawned. /// See [`crate::lifecycle::ComponentHooks::on_despawn`] for more information. -#[derive(Event, EntityEvent, Debug)] +#[derive(Event, EntityEvent, Debug, Clone)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Debug))] #[doc(alias = "OnDespawn")] diff --git a/crates/bevy_ecs/src/name.rs b/crates/bevy_ecs/src/name.rs index cd2e946678..67719ca18d 100644 --- a/crates/bevy_ecs/src/name.rs +++ b/crates/bevy_ecs/src/name.rs @@ -141,7 +141,7 @@ pub struct NameOrEntity { pub entity: Entity, } -impl<'a> core::fmt::Display for NameOrEntityItem<'a> { +impl<'w, 's> core::fmt::Display for NameOrEntityItem<'w, 's> { #[inline(always)] fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { match self.name { @@ -274,9 +274,9 @@ mod tests { let e2 = world.spawn(name.clone()).id(); let mut query = world.query::(); let d1 = query.get(&world, e1).unwrap(); - let d2 = query.get(&world, e2).unwrap(); // NameOrEntity Display for entities without a Name should be {index}v{generation} assert_eq!(d1.to_string(), "0v0"); + let d2 = query.get(&world, e2).unwrap(); // NameOrEntity Display for entities with a Name should be the Name assert_eq!(d2.to_string(), "MyName"); } diff --git a/crates/bevy_ecs/src/observer/entity_observer.rs b/crates/bevy_ecs/src/observer/entity_observer.rs index 5530b1f0fc..23be0e9672 100644 --- a/crates/bevy_ecs/src/observer/entity_observer.rs +++ b/crates/bevy_ecs/src/observer/entity_observer.rs @@ -97,7 +97,7 @@ fn component_clone_observed_by(_source: &SourceComponent, ctx: &mut ComponentClo let event_types = observer_state.descriptor.events.clone(); let components = observer_state.descriptor.components.clone(); for event_type in event_types { - let observers = world.observers.get_observers(event_type); + let observers = world.observers.get_observers_mut(event_type); if components.is_empty() { if let Some(map) = observers.entity_observers.get(&source).cloned() { observers.entity_observers.insert(target, map); @@ -108,8 +108,10 @@ fn component_clone_observed_by(_source: &SourceComponent, ctx: &mut ComponentClo else { continue; }; - if let Some(map) = observers.entity_map.get(&source).cloned() { - observers.entity_map.insert(target, map); + if let Some(map) = + observers.entity_component_observers.get(&source).cloned() + { + observers.entity_component_observers.insert(target, map); } } } diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index 1592c7446b..06da399de6 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -515,40 +515,72 @@ impl ObserverTrigger { } } -// Map between an observer entity and its runner -type ObserverMap = EntityHashMap; +/// Map between an observer entity and its [`ObserverRunner`] +pub type ObserverMap = EntityHashMap; -/// Collection of [`ObserverRunner`] for [`Observer`] registered to a particular trigger targeted at a specific component. +/// Collection of [`ObserverRunner`] for [`Observer`] registered to a particular event targeted at a specific component. /// /// This is stored inside of [`CachedObservers`]. #[derive(Default, Debug)] pub struct CachedComponentObservers { - // Observers listening to triggers targeting this component - map: ObserverMap, - // Observers listening to triggers targeting this component on a specific entity - entity_map: EntityHashMap, + // Observers listening to events targeting this component, but not a specific entity + global_observers: ObserverMap, + // Observers listening to events targeting this component on a specific entity + entity_component_observers: EntityHashMap, } -/// Collection of [`ObserverRunner`] for [`Observer`] registered to a particular trigger. +impl CachedComponentObservers { + /// Returns the observers listening for this trigger, regardless of target. + /// These observers will also respond to events targeting specific entities. + pub fn global_observers(&self) -> &ObserverMap { + &self.global_observers + } + + /// Returns the observers listening for this trigger targeting this component on a specific entity. + pub fn entity_component_observers(&self) -> &EntityHashMap { + &self.entity_component_observers + } +} + +/// Collection of [`ObserverRunner`] for [`Observer`] registered to a particular event. /// /// This is stored inside of [`Observers`], specialized for each kind of observer. #[derive(Default, Debug)] pub struct CachedObservers { - // Observers listening for any time this trigger is fired - map: ObserverMap, + // Observers listening for any time this event is fired, regardless of target + // This will also respond to events targeting specific components or entities + global_observers: ObserverMap, // Observers listening for this trigger fired at a specific component component_observers: HashMap, // Observers listening for this trigger fired at a specific entity entity_observers: EntityHashMap, } +impl CachedObservers { + /// Returns the observers listening for this trigger, regardless of target. + /// These observers will also respond to events targeting specific components or entities. + pub fn global_observers(&self) -> &ObserverMap { + &self.global_observers + } + + /// Returns the observers listening for this trigger targeting components. + pub fn get_component_observers(&self) -> &HashMap { + &self.component_observers + } + + /// Returns the observers listening for this trigger targeting entities. + pub fn entity_observers(&self) -> &HashMap { + &self.component_observers + } +} + /// An internal lookup table tracking all of the observers in the world. /// /// Stores a cache mapping trigger ids to the registered observers. /// Some observer kinds (like [lifecycle](crate::lifecycle) observers) have a dedicated field, /// saving lookups for the most common triggers. /// -/// This is stored as a field of the [`World`]. +/// This can be accessed via [`World::observers`]. #[derive(Default, Debug)] pub struct Observers { // Cached ECS observers to save a lookup most common triggers. @@ -562,7 +594,7 @@ pub struct Observers { } impl Observers { - pub(crate) fn get_observers(&mut self, event_type: ComponentId) -> &mut CachedObservers { + pub(crate) fn get_observers_mut(&mut self, event_type: ComponentId) -> &mut CachedObservers { use crate::lifecycle::*; match event_type { @@ -575,7 +607,11 @@ impl Observers { } } - pub(crate) fn try_get_observers(&self, event_type: ComponentId) -> Option<&CachedObservers> { + /// Attempts to get the observers for the given `event_type`. + /// + /// When accessing the observers for lifecycle events, such as [`Add`], [`Insert`], [`Replace`], [`Remove`], and [`Despawn`], + /// use the [`ComponentId`] constants from the [`lifecycle`](crate::lifecycle) module. + pub fn try_get_observers(&self, event_type: ComponentId) -> Option<&CachedObservers> { use crate::lifecycle::*; match event_type { @@ -630,7 +666,10 @@ impl Observers { ); }; // Trigger observers listening for any kind of this trigger - observers.map.iter().for_each(&mut trigger_observer); + observers + .global_observers + .iter() + .for_each(&mut trigger_observer); // Trigger entity observers listening for this kind of trigger if let Some(target_entity) = current_target { @@ -643,12 +682,15 @@ impl Observers { trigger_for_components.for_each(|id| { if let Some(component_observers) = observers.component_observers.get(&id) { component_observers - .map + .global_observers .iter() .for_each(&mut trigger_observer); if let Some(target_entity) = current_target { - if let Some(map) = component_observers.entity_map.get(&target_entity) { + if let Some(map) = component_observers + .entity_component_observers + .get(&target_entity) + { map.iter().for_each(&mut trigger_observer); } } @@ -926,10 +968,12 @@ impl World { let descriptor = &observer_state.descriptor; for &event_type in &descriptor.events { - let cache = observers.get_observers(event_type); + let cache = observers.get_observers_mut(event_type); if descriptor.components.is_empty() && descriptor.entities.is_empty() { - cache.map.insert(observer_entity, observer_state.runner); + cache + .global_observers + .insert(observer_entity, observer_state.runner); } else if descriptor.components.is_empty() { // Observer is not targeting any components so register it as an entity observer for &watched_entity in &observer_state.descriptor.entities { @@ -951,11 +995,16 @@ impl World { }); if descriptor.entities.is_empty() { // Register for all triggers targeting the component - observers.map.insert(observer_entity, observer_state.runner); + observers + .global_observers + .insert(observer_entity, observer_state.runner); } else { // Register for each watched entity for &watched_entity in &descriptor.entities { - let map = observers.entity_map.entry(watched_entity).or_default(); + let map = observers + .entity_component_observers + .entry(watched_entity) + .or_default(); map.insert(observer_entity, observer_state.runner); } } @@ -970,9 +1019,9 @@ impl World { let observers = &mut self.observers; for &event_type in &descriptor.events { - let cache = observers.get_observers(event_type); + let cache = observers.get_observers_mut(event_type); if descriptor.components.is_empty() && descriptor.entities.is_empty() { - cache.map.remove(&entity); + cache.global_observers.remove(&entity); } else if descriptor.components.is_empty() { for watched_entity in &descriptor.entities { // This check should be unnecessary since this observer hasn't been unregistered yet @@ -990,20 +1039,24 @@ impl World { continue; }; if descriptor.entities.is_empty() { - observers.map.remove(&entity); + observers.global_observers.remove(&entity); } else { for watched_entity in &descriptor.entities { - let Some(map) = observers.entity_map.get_mut(watched_entity) else { + let Some(map) = + observers.entity_component_observers.get_mut(watched_entity) + else { continue; }; map.remove(&entity); if map.is_empty() { - observers.entity_map.remove(watched_entity); + observers.entity_component_observers.remove(watched_entity); } } } - if observers.map.is_empty() && observers.entity_map.is_empty() { + if observers.global_observers.is_empty() + && observers.entity_component_observers.is_empty() + { cache.component_observers.remove(component); if let Some(flag) = Observers::is_archetype_cached(event_type) { if let Some(by_component) = archetypes.by_component.get(component) { @@ -1078,7 +1131,7 @@ mod tests { struct ChildOf(Entity); impl Traversal for &'_ ChildOf { - fn traverse(item: Self::Item<'_>, _: &D) -> Option { + fn traverse(item: Self::Item<'_, '_>, _: &D) -> Option { Some(item.0) } } diff --git a/crates/bevy_ecs/src/observer/runner.rs b/crates/bevy_ecs/src/observer/runner.rs index 4fd9f23556..d6bffd8f22 100644 --- a/crates/bevy_ecs/src/observer/runner.rs +++ b/crates/bevy_ecs/src/observer/runner.rs @@ -1,4 +1,5 @@ use alloc::{boxed::Box, vec}; +use bevy_utils::prelude::DebugName; use core::any::Any; use crate::{ @@ -194,7 +195,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate: pub struct Observer { hook_on_add: ComponentHook, error_handler: Option, - system: Box, + system: Box, pub(crate) descriptor: ObserverDescriptor, pub(crate) last_trigger_id: u32, pub(crate) despawned_watched_entities: u32, @@ -232,7 +233,7 @@ impl Observer { /// Creates a new [`Observer`] with custom runner, this is mostly used for dynamic event observer pub fn with_dynamic_runner(runner: ObserverRunner) -> Self { Self { - system: Box::new(|| {}), + system: Box::new(IntoSystem::into_system(|| {})), descriptor: Default::default(), hook_on_add: |mut world, hook_context| { let default_error_handler = world.default_error_handler(); @@ -299,6 +300,11 @@ impl Observer { pub fn descriptor(&self) -> &ObserverDescriptor { &self.descriptor } + + /// Returns the name of the [`Observer`]'s system . + pub fn system_name(&self) -> DebugName { + self.system.system_name() + } } impl Component for Observer { @@ -364,7 +370,8 @@ fn observer_system_runner>( // - observer was triggered so must have an `Observer` component. // - observer cannot be dropped or mutated until after the system pointer is already dropped. let system: *mut dyn ObserverSystem = unsafe { - let system = state.system.downcast_mut::().debug_checked_unwrap(); + let system: &mut dyn Any = state.system.as_mut(); + let system = system.downcast_mut::().debug_checked_unwrap(); &mut *system }; @@ -413,6 +420,16 @@ fn observer_system_runner>( } } +trait AnyNamedSystem: Any + Send + Sync + 'static { + fn system_name(&self) -> DebugName; +} + +impl AnyNamedSystem for T { + fn system_name(&self) -> DebugName { + self.name() + } +} + /// A [`ComponentHook`] used by [`Observer`] to handle its [`on-add`](`crate::lifecycle::ComponentHooks::on_add`). /// /// This function exists separate from [`Observer`] to allow [`Observer`] to have its type parameters @@ -431,11 +448,12 @@ fn hook_on_add>( B::component_ids(&mut world.components_registrator(), &mut |id| { components.push(id); }); - if let Some(mut observe) = world.get_mut::(entity) { - observe.descriptor.events.push(event_id); - observe.descriptor.components.extend(components); + if let Some(mut observer) = world.get_mut::(entity) { + observer.descriptor.events.push(event_id); + observer.descriptor.components.extend(components); - let system: *mut dyn ObserverSystem = observe.system.downcast_mut::().unwrap(); + let system: &mut dyn Any = observer.system.as_mut(); + let system: *mut dyn ObserverSystem = system.downcast_mut::().unwrap(); // SAFETY: World reference is exclusive and initialize does not touch system, so references do not alias unsafe { (*system).initialize(world); diff --git a/crates/bevy_ecs/src/query/access.rs b/crates/bevy_ecs/src/query/access.rs index 9c63cb5a74..0c5b29f715 100644 --- a/crates/bevy_ecs/src/query/access.rs +++ b/crates/bevy_ecs/src/query/access.rs @@ -4,7 +4,6 @@ use crate::world::World; use alloc::{format, string::String, vec, vec::Vec}; use core::{fmt, fmt::Debug, marker::PhantomData}; use derive_more::From; -use disqualified::ShortName; use fixedbitset::FixedBitSet; use thiserror::Error; @@ -999,12 +998,11 @@ impl AccessConflicts { .map(|index| { format!( "{}", - ShortName( - &world - .components - .get_name(ComponentId::get_sparse_set_index(index)) - .unwrap() - ) + world + .components + .get_name(ComponentId::get_sparse_set_index(index)) + .unwrap() + .shortname() ) }) .collect::>() diff --git a/crates/bevy_ecs/src/query/error.rs b/crates/bevy_ecs/src/query/error.rs index f3579351cc..d643e0815c 100644 --- a/crates/bevy_ecs/src/query/error.rs +++ b/crates/bevy_ecs/src/query/error.rs @@ -1,3 +1,6 @@ +use bevy_utils::prelude::DebugName; +use thiserror::Error; + use crate::{ archetype::ArchetypeId, entity::{ConstructedEntityDoesNotExistError, Entity}, @@ -28,10 +31,10 @@ pub enum QueryEntityError { pub enum QuerySingleError { /// No entity fits the query. #[error("No entities fit the query {0}")] - NoEntities(&'static str), + NoEntities(DebugName), /// Multiple entities fit the query. #[error("Multiple entities fit the query {0}")] - MultipleEntities(&'static str), + MultipleEntities(DebugName), } #[cfg(test)] diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 68bdf40b5f..a32f5fed28 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -12,6 +12,7 @@ use crate::{ }, }; use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; +use bevy_utils::prelude::DebugName; use core::{cell::UnsafeCell, marker::PhantomData, panic::Location}; use smallvec::SmallVec; use variadics_please::all_tuples; @@ -163,7 +164,7 @@ use variadics_please::all_tuples; /// } /// /// // `HealthQueryItem` is only available when accessing the query with mutable methods. -/// impl<'w> HealthQueryItem<'w> { +/// impl<'w, 's> HealthQueryItem<'w, 's> { /// fn damage(&mut self, value: f32) { /// self.health.0 -= value; /// } @@ -174,7 +175,7 @@ use variadics_please::all_tuples; /// } /// /// // `HealthQueryReadOnlyItem` is only available when accessing the query with immutable methods. -/// impl<'w> HealthQueryReadOnlyItem<'w> { +/// impl<'w, 's> HealthQueryReadOnlyItem<'w, 's> { /// fn total(&self) -> f32 { /// self.health.0 + self.buff.map_or(0.0, |Buff(buff)| *buff) /// } @@ -290,10 +291,12 @@ pub unsafe trait QueryData: WorldQuery { /// The item returned by this [`WorldQuery`] /// This will be the data retrieved by the query, /// and is visible to the end user when calling e.g. `Query::get`. - type Item<'a>; + type Item<'w, 's>; /// This function manually implements subtyping for the query items. - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort>; + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's>; /// Offers additional access above what we requested in `update_component_access`. /// Implementations may add additional access that is a subset of `available_access` @@ -322,11 +325,12 @@ pub unsafe trait QueryData: WorldQuery { /// - Must always be called _after_ [`WorldQuery::set_table`] or [`WorldQuery::set_archetype`]. `entity` and /// `table_row` must be in the range of the current table and archetype. /// - There must not be simultaneous conflicting component access registered in `update_component_access`. - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, - ) -> Self::Item<'w>; + ) -> Self::Item<'w, 's>; } /// A [`QueryData`] that is read only. @@ -337,9 +341,20 @@ pub unsafe trait QueryData: WorldQuery { pub unsafe trait ReadOnlyQueryData: QueryData {} /// The item type returned when a [`WorldQuery`] is iterated over -pub type QueryItem<'w, Q> = ::Item<'w>; +pub type QueryItem<'w, 's, Q> = ::Item<'w, 's>; /// The read-only variant of the item type returned when a [`QueryData`] is iterated over immutably -pub type ROQueryItem<'w, D> = QueryItem<'w, ::ReadOnly>; +pub type ROQueryItem<'w, 's, D> = QueryItem<'w, 's, ::ReadOnly>; + +/// A [`QueryData`] that does not borrow from its [`QueryState`](crate::query::QueryState). +/// +/// This is implemented by most `QueryData` types. +/// The main exceptions are [`FilteredEntityRef`], [`FilteredEntityMut`], [`EntityRefExcept`], and [`EntityMutExcept`], +/// which borrow an access list from their query state. +/// Consider using a full [`EntityRef`] or [`EntityMut`] if you would need those. +pub trait ReleaseStateQueryData: QueryData { + /// Releases the borrow from the query state by converting an item to have a `'static` state lifetime. + fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static>; +} /// SAFETY: /// `update_component_access` does nothing. @@ -350,9 +365,9 @@ unsafe impl WorldQuery for Entity { fn shrink_fetch<'wlong: 'wshort, 'wshort>(_: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {} - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( _world: UnsafeWorldCell<'w>, - _state: &Self::State, + _state: &'s Self::State, _last_run: Tick, _this_run: Tick, ) -> Self::Fetch<'w> { @@ -361,16 +376,20 @@ unsafe impl WorldQuery for Entity { const IS_DENSE: bool = true; #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( _fetch: &mut Self::Fetch<'w>, - _state: &Self::State, + _state: &'s Self::State, _archetype: &'w Archetype, _table: &Table, ) { } #[inline] - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + unsafe fn set_table<'w, 's>( + _fetch: &mut Self::Fetch<'w>, + _state: &'s Self::State, + _table: &'w Table, + ) { } fn update_component_access(_state: &Self::State, _access: &mut FilteredAccess) {} @@ -394,18 +413,21 @@ unsafe impl QueryData for Entity { const IS_READ_ONLY: bool = true; type ReadOnly = Self; - type Item<'w> = Entity; + type Item<'w, 's> = Entity; - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, _fetch: &mut Self::Fetch<'w>, entity: Entity, _table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { entity } } @@ -413,6 +435,12 @@ unsafe impl QueryData for Entity { /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for Entity {} +impl ReleaseStateQueryData for Entity { + fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { + item + } +} + /// SAFETY: /// `update_component_access` does nothing. /// This is sound because `fetch` does not access components. @@ -424,9 +452,9 @@ unsafe impl WorldQuery for EntityLocation { fetch } - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - _state: &Self::State, + _state: &'s Self::State, _last_run: Tick, _this_run: Tick, ) -> Self::Fetch<'w> { @@ -438,16 +466,20 @@ unsafe impl WorldQuery for EntityLocation { const IS_DENSE: bool = true; #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( _fetch: &mut Self::Fetch<'w>, - _state: &Self::State, + _state: &'s Self::State, _archetype: &'w Archetype, _table: &Table, ) { } #[inline] - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + unsafe fn set_table<'w, 's>( + _fetch: &mut Self::Fetch<'w>, + _state: &'s Self::State, + _table: &'w Table, + ) { } fn update_component_access(_state: &Self::State, _access: &mut FilteredAccess) {} @@ -470,18 +502,21 @@ unsafe impl WorldQuery for EntityLocation { unsafe impl QueryData for EntityLocation { const IS_READ_ONLY: bool = true; type ReadOnly = Self; - type Item<'w> = EntityLocation; + type Item<'w, 's> = EntityLocation; - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, _table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { // SAFETY: `fetch` must be called with an entity that exists in the world unsafe { fetch.get_constructed(entity).debug_checked_unwrap() } } @@ -490,6 +525,12 @@ unsafe impl QueryData for EntityLocation { /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for EntityLocation {} +impl ReleaseStateQueryData for EntityLocation { + fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { + item + } +} + /// The `SpawnDetails` query parameter fetches the [`Tick`] the entity was spawned at. /// /// To evaluate whether the spawn happened since the last time the system ran, the system @@ -570,9 +611,9 @@ unsafe impl WorldQuery for SpawnDetails { fetch } - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - _state: &Self::State, + _state: &'s Self::State, last_run: Tick, this_run: Tick, ) -> Self::Fetch<'w> { @@ -586,16 +627,20 @@ unsafe impl WorldQuery for SpawnDetails { const IS_DENSE: bool = true; #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( _fetch: &mut Self::Fetch<'w>, - _state: &Self::State, + _state: &'s Self::State, _archetype: &'w Archetype, _table: &'w Table, ) { } #[inline] - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + unsafe fn set_table<'w, 's>( + _fetch: &mut Self::Fetch<'w>, + _state: &'s Self::State, + _table: &'w Table, + ) { } fn update_component_access(_state: &Self::State, _access: &mut FilteredAccess) {} @@ -620,18 +665,21 @@ unsafe impl WorldQuery for SpawnDetails { unsafe impl QueryData for SpawnDetails { const IS_READ_ONLY: bool = true; type ReadOnly = Self; - type Item<'w> = Self; + type Item<'w, 's> = Self; - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, _table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { // SAFETY: only living entities are queried let (spawned_by, spawned_at) = unsafe { fetch @@ -650,6 +698,12 @@ unsafe impl QueryData for SpawnDetails { /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for SpawnDetails {} +impl ReleaseStateQueryData for SpawnDetails { + fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { + item + } +} + /// The [`WorldQuery::Fetch`] type for WorldQueries that can fetch multiple components from an entity /// ([`EntityRef`], [`EntityMut`], etc.) #[derive(Copy, Clone)] @@ -672,9 +726,9 @@ unsafe impl<'a> WorldQuery for EntityRef<'a> { fetch } - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - _state: &Self::State, + _state: &'s Self::State, last_run: Tick, this_run: Tick, ) -> Self::Fetch<'w> { @@ -688,16 +742,20 @@ unsafe impl<'a> WorldQuery for EntityRef<'a> { const IS_DENSE: bool = true; #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( _fetch: &mut Self::Fetch<'w>, - _state: &Self::State, + _state: &'s Self::State, _archetype: &'w Archetype, _table: &Table, ) { } #[inline] - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + unsafe fn set_table<'w, 's>( + _fetch: &mut Self::Fetch<'w>, + _state: &'s Self::State, + _table: &'w Table, + ) { } fn update_component_access(_state: &Self::State, access: &mut FilteredAccess) { @@ -726,18 +784,21 @@ unsafe impl<'a> WorldQuery for EntityRef<'a> { unsafe impl<'a> QueryData for EntityRef<'a> { const IS_READ_ONLY: bool = true; type ReadOnly = Self; - type Item<'w> = EntityRef<'w>; + type Item<'w, 's> = EntityRef<'w>; - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, _table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { // SAFETY: `fetch` must be called with an entity that exists in the world let cell = unsafe { fetch @@ -753,6 +814,12 @@ unsafe impl<'a> QueryData for EntityRef<'a> { /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for EntityRef<'_> {} +impl ReleaseStateQueryData for EntityRef<'_> { + fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { + item + } +} + /// SAFETY: The accesses of `Self::ReadOnly` are a subset of the accesses of `Self` unsafe impl<'a> WorldQuery for EntityMut<'a> { type Fetch<'w> = EntityFetch<'w>; @@ -762,9 +829,9 @@ unsafe impl<'a> WorldQuery for EntityMut<'a> { fetch } - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - _state: &Self::State, + _state: &'s Self::State, last_run: Tick, this_run: Tick, ) -> Self::Fetch<'w> { @@ -778,16 +845,20 @@ unsafe impl<'a> WorldQuery for EntityMut<'a> { const IS_DENSE: bool = true; #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( _fetch: &mut Self::Fetch<'w>, - _state: &Self::State, + _state: &'s Self::State, _archetype: &'w Archetype, _table: &Table, ) { } #[inline] - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + unsafe fn set_table<'w, 's>( + _fetch: &mut Self::Fetch<'w>, + _state: &'s Self::State, + _table: &'w Table, + ) { } fn update_component_access(_state: &Self::State, access: &mut FilteredAccess) { @@ -816,18 +887,21 @@ unsafe impl<'a> WorldQuery for EntityMut<'a> { unsafe impl<'a> QueryData for EntityMut<'a> { const IS_READ_ONLY: bool = false; type ReadOnly = EntityRef<'a>; - type Item<'w> = EntityMut<'w>; + type Item<'w, 's> = EntityMut<'w>; - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, _table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { // SAFETY: `fetch` must be called with an entity that exists in the world let cell = unsafe { fetch @@ -840,6 +914,12 @@ unsafe impl<'a> QueryData for EntityMut<'a> { } } +impl ReleaseStateQueryData for EntityMut<'_> { + fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { + item + } +} + /// SAFETY: The accesses of `Self::ReadOnly` are a subset of the accesses of `Self` unsafe impl<'a> WorldQuery for FilteredEntityRef<'a> { type Fetch<'w> = (EntityFetch<'w>, Access); @@ -851,9 +931,9 @@ unsafe impl<'a> WorldQuery for FilteredEntityRef<'a> { const IS_DENSE: bool = false; - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - _state: &Self::State, + _state: &'s Self::State, last_run: Tick, this_run: Tick, ) -> Self::Fetch<'w> { @@ -870,9 +950,9 @@ unsafe impl<'a> WorldQuery for FilteredEntityRef<'a> { } #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( fetch: &mut Self::Fetch<'w>, - state: &Self::State, + state: &'s Self::State, _: &'w Archetype, _table: &Table, ) { @@ -880,7 +960,7 @@ unsafe impl<'a> WorldQuery for FilteredEntityRef<'a> { } #[inline] - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, _: &'w Table) { + unsafe fn set_table<'w, 's>(fetch: &mut Self::Fetch<'w>, state: &'s Self::State, _: &'w Table) { fetch.1.clone_from(state); } @@ -915,9 +995,11 @@ unsafe impl<'a> WorldQuery for FilteredEntityRef<'a> { unsafe impl<'a> QueryData for FilteredEntityRef<'a> { const IS_READ_ONLY: bool = true; type ReadOnly = Self; - type Item<'w> = FilteredEntityRef<'w>; + type Item<'w, 's> = FilteredEntityRef<'w>; - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item } @@ -941,11 +1023,12 @@ unsafe impl<'a> QueryData for FilteredEntityRef<'a> { } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, (fetch, access): &mut Self::Fetch<'w>, entity: Entity, _table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { // SAFETY: `fetch` must be called with an entity that exists in the world let cell = unsafe { fetch @@ -972,9 +1055,9 @@ unsafe impl<'a> WorldQuery for FilteredEntityMut<'a> { const IS_DENSE: bool = false; - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - _state: &Self::State, + _state: &'s Self::State, last_run: Tick, this_run: Tick, ) -> Self::Fetch<'w> { @@ -991,9 +1074,9 @@ unsafe impl<'a> WorldQuery for FilteredEntityMut<'a> { } #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( fetch: &mut Self::Fetch<'w>, - state: &Self::State, + state: &'s Self::State, _: &'w Archetype, _table: &Table, ) { @@ -1001,7 +1084,7 @@ unsafe impl<'a> WorldQuery for FilteredEntityMut<'a> { } #[inline] - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, _: &'w Table) { + unsafe fn set_table<'w, 's>(fetch: &mut Self::Fetch<'w>, state: &'s Self::State, _: &'w Table) { fetch.1.clone_from(state); } @@ -1036,9 +1119,11 @@ unsafe impl<'a> WorldQuery for FilteredEntityMut<'a> { unsafe impl<'a> QueryData for FilteredEntityMut<'a> { const IS_READ_ONLY: bool = false; type ReadOnly = FilteredEntityRef<'a>; - type Item<'w> = FilteredEntityMut<'w>; + type Item<'w, 's> = FilteredEntityMut<'w>; - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item } @@ -1060,11 +1145,12 @@ unsafe impl<'a> QueryData for FilteredEntityMut<'a> { } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, (fetch, access): &mut Self::Fetch<'w>, entity: Entity, _table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { // SAFETY: `fetch` must be called with an entity that exists in the world let cell = unsafe { fetch @@ -1091,9 +1177,9 @@ where fetch } - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - _: &Self::State, + _: &'s Self::State, last_run: Tick, this_run: Tick, ) -> Self::Fetch<'w> { @@ -1106,15 +1192,15 @@ where const IS_DENSE: bool = true; - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( _: &mut Self::Fetch<'w>, - _: &Self::State, + _: &'s Self::State, _: &'w Archetype, _: &'w Table, ) { } - unsafe fn set_table<'w>(_: &mut Self::Fetch<'w>, _: &Self::State, _: &'w Table) {} + unsafe fn set_table<'w, 's>(_: &mut Self::Fetch<'w>, _: &'s Self::State, _: &'w Table) {} fn update_component_access( state: &Self::State, @@ -1130,7 +1216,7 @@ where assert!( access.is_compatible(&my_access), "`EntityRefExcept<{}>` conflicts with a previous access in this query.", - core::any::type_name::(), + DebugName::type_name::(), ); access.extend(&my_access); } @@ -1161,17 +1247,20 @@ where { const IS_READ_ONLY: bool = true; type ReadOnly = Self; - type Item<'w> = EntityRefExcept<'w, B>; + type Item<'w, 's> = EntityRefExcept<'w, B>; - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item } - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, _: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { let cell = fetch .world .get_entity_with_ticks(entity, fetch.last_run, fetch.this_run) @@ -1198,9 +1287,9 @@ where fetch } - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - _: &Self::State, + _: &'s Self::State, last_run: Tick, this_run: Tick, ) -> Self::Fetch<'w> { @@ -1213,15 +1302,15 @@ where const IS_DENSE: bool = true; - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( _: &mut Self::Fetch<'w>, - _: &Self::State, + _: &'s Self::State, _: &'w Archetype, _: &'w Table, ) { } - unsafe fn set_table<'w>(_: &mut Self::Fetch<'w>, _: &Self::State, _: &'w Table) {} + unsafe fn set_table<'w, 's>(_: &mut Self::Fetch<'w>, _: &'s Self::State, _: &'w Table) {} fn update_component_access( state: &Self::State, @@ -1237,7 +1326,7 @@ where assert!( access.is_compatible(&my_access), "`EntityMutExcept<{}>` conflicts with a previous access in this query.", - core::any::type_name::() + DebugName::type_name::() ); access.extend(&my_access); } @@ -1269,17 +1358,20 @@ where { const IS_READ_ONLY: bool = false; type ReadOnly = EntityRefExcept<'a, B>; - type Item<'w> = EntityMutExcept<'w, B>; + type Item<'w, 's> = EntityMutExcept<'w, B>; - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item } - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, _: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { let cell = fetch .world .get_entity_with_ticks(entity, fetch.last_run, fetch.this_run) @@ -1299,9 +1391,9 @@ unsafe impl WorldQuery for &Archetype { fetch } - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - _state: &Self::State, + _state: &'s Self::State, _last_run: Tick, _this_run: Tick, ) -> Self::Fetch<'w> { @@ -1313,16 +1405,20 @@ unsafe impl WorldQuery for &Archetype { const IS_DENSE: bool = true; #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( _fetch: &mut Self::Fetch<'w>, - _state: &Self::State, + _state: &'s Self::State, _archetype: &'w Archetype, _table: &Table, ) { } #[inline] - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + unsafe fn set_table<'w, 's>( + _fetch: &mut Self::Fetch<'w>, + _state: &'s Self::State, + _table: &'w Table, + ) { } fn update_component_access(_state: &Self::State, _access: &mut FilteredAccess) {} @@ -1345,18 +1441,21 @@ unsafe impl WorldQuery for &Archetype { unsafe impl QueryData for &Archetype { const IS_READ_ONLY: bool = true; type ReadOnly = Self; - type Item<'w> = &'w Archetype; + type Item<'w, 's> = &'w Archetype; - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, _table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { let (entities, archetypes) = *fetch; // SAFETY: `fetch` must be called with an entity that exists in the world let location = unsafe { entities.get_constructed(entity).debug_checked_unwrap() }; @@ -1368,6 +1467,12 @@ unsafe impl QueryData for &Archetype { /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for &Archetype {} +impl ReleaseStateQueryData for &Archetype { + fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { + item + } +} + /// The [`WorldQuery::Fetch`] type for `& T`. pub struct ReadFetch<'w, T: Component> { components: StorageSwitch< @@ -1400,7 +1505,7 @@ unsafe impl WorldQuery for &T { } #[inline] - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, &component_id: &ComponentId, _last_run: Tick, @@ -1465,7 +1570,7 @@ unsafe impl WorldQuery for &T { assert!( !access.access().has_component_write(component_id), "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", - core::any::type_name::(), + DebugName::type_name::(), ); access.add_component_read(component_id); } @@ -1490,18 +1595,21 @@ unsafe impl WorldQuery for &T { unsafe impl QueryData for &T { const IS_READ_ONLY: bool = true; type ReadOnly = Self; - type Item<'w> = &'w T; + type Item<'w, 's> = &'w T; - fn shrink<'wlong: 'wshort, 'wshort>(item: &'wlong T) -> &'wshort T { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { fetch.components.extract( |table| { // SAFETY: set_table was previously called @@ -1527,6 +1635,12 @@ unsafe impl QueryData for &T { /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for &T {} +impl ReleaseStateQueryData for &T { + fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { + item + } +} + #[doc(hidden)] pub struct RefFetch<'w, T: Component> { components: StorageSwitch< @@ -1567,7 +1681,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { } #[inline] - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, &component_id: &ComponentId, last_run: Tick, @@ -1641,7 +1755,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { assert!( !access.access().has_component_write(component_id), "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", - core::any::type_name::(), + DebugName::type_name::(), ); access.add_component_read(component_id); } @@ -1666,18 +1780,21 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { const IS_READ_ONLY: bool = true; type ReadOnly = Self; - type Item<'w> = Ref<'w, T>; + type Item<'w, 's> = Ref<'w, T>; - fn shrink<'wlong: 'wshort, 'wshort>(item: Ref<'wlong, T>) -> Ref<'wshort, T> { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { fetch.components.extract( |table| { // SAFETY: set_table was previously called @@ -1726,6 +1843,12 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { /// SAFETY: access is read only unsafe impl<'__w, T: Component> ReadOnlyQueryData for Ref<'__w, T> {} +impl ReleaseStateQueryData for Ref<'_, T> { + fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { + item + } +} + /// The [`WorldQuery::Fetch`] type for `&mut T`. pub struct WriteFetch<'w, T: Component> { components: StorageSwitch< @@ -1766,7 +1889,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { } #[inline] - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, &component_id: &ComponentId, last_run: Tick, @@ -1840,7 +1963,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { assert!( !access.access().has_component_read(component_id), "&mut {} conflicts with a previous access in this query. Mutable component access must be unique.", - core::any::type_name::(), + DebugName::type_name::(), ); access.add_component_write(component_id); } @@ -1865,18 +1988,21 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { unsafe impl<'__w, T: Component> QueryData for &'__w mut T { const IS_READ_ONLY: bool = false; type ReadOnly = &'__w T; - type Item<'w> = Mut<'w, T>; + type Item<'w, 's> = Mut<'w, T>; - fn shrink<'wlong: 'wshort, 'wshort>(item: Mut<'wlong, T>) -> Mut<'wshort, T> { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { fetch.components.extract( |table| { // SAFETY: set_table was previously called @@ -1922,6 +2048,12 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T } } +impl> ReleaseStateQueryData for &mut T { + fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { + item + } +} + /// When `Mut` is used in a query, it will be converted to `Ref` when transformed into its read-only form, providing access to change detection methods. /// /// By contrast `&mut T` will result in a `Mut` item in mutable form to record mutations, but result in a bare `&T` in read-only form. @@ -1941,7 +2073,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Mut<'__w, T> { #[inline] // Forwarded to `&mut T` - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, state: &ComponentId, last_run: Tick, @@ -1980,7 +2112,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Mut<'__w, T> { assert!( !access.access().has_component_read(component_id), "Mut<{}> conflicts with a previous access in this query. Mutable component access mut be unique.", - core::any::type_name::(), + DebugName::type_name::(), ); access.add_component_write(component_id); } @@ -2008,23 +2140,32 @@ unsafe impl<'__w, T: Component> WorldQuery for Mut<'__w, T> { unsafe impl<'__w, T: Component> QueryData for Mut<'__w, T> { const IS_READ_ONLY: bool = false; type ReadOnly = Ref<'__w, T>; - type Item<'w> = Mut<'w, T>; + type Item<'w, 's> = Mut<'w, T>; // Forwarded to `&mut T` - fn shrink<'wlong: 'wshort, 'wshort>(item: Mut<'wlong, T>) -> Mut<'wshort, T> { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { <&mut T as QueryData>::shrink(item) } #[inline(always)] // Forwarded to `&mut T` - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + state: &'s Self::State, // Rust complains about lifetime bounds not matching the trait if I directly use `WriteFetch<'w, T>` right here. // But it complains nowhere else in the entire trait implementation. fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, - ) -> Mut<'w, T> { - <&mut T as QueryData>::fetch(fetch, entity, table_row) + ) -> Self::Item<'w, 's> { + <&mut T as QueryData>::fetch(state, fetch, entity, table_row) + } +} + +impl> ReleaseStateQueryData for Mut<'_, T> { + fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { + item } } @@ -2059,9 +2200,9 @@ unsafe impl WorldQuery for Option { } #[inline] - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - state: &T::State, + state: &'s T::State, last_run: Tick, this_run: Tick, ) -> OptionFetch<'w, T> { @@ -2075,9 +2216,9 @@ unsafe impl WorldQuery for Option { const IS_DENSE: bool = T::IS_DENSE; #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( fetch: &mut OptionFetch<'w, T>, - state: &T::State, + state: &'s T::State, archetype: &'w Archetype, table: &'w Table, ) { @@ -2091,7 +2232,11 @@ unsafe impl WorldQuery for Option { } #[inline] - unsafe fn set_table<'w>(fetch: &mut OptionFetch<'w, T>, state: &T::State, table: &'w Table) { + unsafe fn set_table<'w, 's>( + fetch: &mut OptionFetch<'w, T>, + state: &'s T::State, + table: &'w Table, + ) { fetch.matches = T::matches_component_set(state, &|id| table.has_column(id)); if fetch.matches { // SAFETY: The invariants are upheld by the caller. @@ -2136,28 +2281,37 @@ unsafe impl WorldQuery for Option { unsafe impl QueryData for Option { const IS_READ_ONLY: bool = T::IS_READ_ONLY; type ReadOnly = Option; - type Item<'w> = Option>; + type Item<'w, 's> = Option>; - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item.map(T::shrink) } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { fetch .matches // SAFETY: The invariants are upheld by the caller. - .then(|| unsafe { T::fetch(&mut fetch.fetch, entity, table_row) }) + .then(|| unsafe { T::fetch(state, &mut fetch.fetch, entity, table_row) }) } } /// SAFETY: [`OptionFetch`] is read only because `T` is read only unsafe impl ReadOnlyQueryData for Option {} +impl ReleaseStateQueryData for Option { + fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { + item.map(T::release_state) + } +} + /// Returns a bool that describes if an entity has the component `T`. /// /// This can be used in a [`Query`](crate::system::Query) if you want to know whether or not entities @@ -2225,7 +2379,7 @@ pub struct Has(PhantomData); impl core::fmt::Debug for Has { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { - write!(f, "Has<{}>", core::any::type_name::()) + write!(f, "Has<{}>", DebugName::type_name::()) } } @@ -2241,9 +2395,9 @@ unsafe impl WorldQuery for Has { } #[inline] - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( _world: UnsafeWorldCell<'w>, - _state: &Self::State, + _state: &'s Self::State, _last_run: Tick, _this_run: Tick, ) -> Self::Fetch<'w> { @@ -2258,9 +2412,9 @@ unsafe impl WorldQuery for Has { }; #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( fetch: &mut Self::Fetch<'w>, - state: &Self::State, + state: &'s Self::State, archetype: &'w Archetype, _table: &Table, ) { @@ -2268,7 +2422,11 @@ unsafe impl WorldQuery for Has { } #[inline] - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) { + unsafe fn set_table<'w, 's>( + fetch: &mut Self::Fetch<'w>, + state: &'s Self::State, + table: &'w Table, + ) { *fetch = table.has_column(*state); } @@ -2300,18 +2458,21 @@ unsafe impl WorldQuery for Has { unsafe impl QueryData for Has { const IS_READ_ONLY: bool = true; type ReadOnly = Self; - type Item<'w> = bool; + type Item<'w, 's> = bool; - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, fetch: &mut Self::Fetch<'w>, _entity: Entity, _table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { *fetch } } @@ -2319,6 +2480,12 @@ unsafe impl QueryData for Has { /// SAFETY: [`Has`] is read only unsafe impl ReadOnlyQueryData for Has {} +impl ReleaseStateQueryData for Has { + fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { + item + } +} + /// The `AnyOf` query parameter fetches entities with any of the component types included in T. /// /// `Query>` is equivalent to `Query<(Option<&A>, Option<&B>, Option<&mut C>), Or<(With, With, With)>>`. @@ -2327,7 +2494,7 @@ unsafe impl ReadOnlyQueryData for Has {} pub struct AnyOf(PhantomData); macro_rules! impl_tuple_query_data { - ($(#[$meta:meta])* $(($name: ident, $state: ident)),*) => { + ($(#[$meta:meta])* $(($name: ident, $item: ident, $state: ident)),*) => { #[expect( clippy::allow_attributes, reason = "This is a tuple-related macro; as such the lints below may not always apply." @@ -2349,9 +2516,9 @@ macro_rules! impl_tuple_query_data { unsafe impl<$($name: QueryData),*> QueryData for ($($name,)*) { const IS_READ_ONLY: bool = true $(&& $name::IS_READ_ONLY)*; type ReadOnly = ($($name::ReadOnly,)*); - type Item<'w> = ($($name::Item<'w>,)*); + type Item<'w, 's> = ($($name::Item<'w, 's>,)*); - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + fn shrink<'wlong: 'wshort, 'wshort, 's>(item: Self::Item<'wlong, 's>) -> Self::Item<'wshort, 's> { let ($($name,)*) = item; ($( $name::shrink($name), @@ -2369,26 +2536,40 @@ macro_rules! impl_tuple_query_data { } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { + let ($($state,)*) = state; let ($($name,)*) = fetch; // SAFETY: The invariants are upheld by the caller. - ($(unsafe { $name::fetch($name, entity, table_row) },)*) + ($(unsafe { $name::fetch($state, $name, entity, table_row) },)*) } } - $(#[$meta])* /// SAFETY: each item in the tuple is read only unsafe impl<$($name: ReadOnlyQueryData),*> ReadOnlyQueryData for ($($name,)*) {} + #[expect( + clippy::allow_attributes, + reason = "This is a tuple-related macro; as such the lints below may not always apply." + )] + #[allow( + clippy::unused_unit, + reason = "Zero-length tuples will generate some function bodies equivalent to `()`; however, this macro is meant for all applicable tuples, and as such it makes no sense to rewrite it just for that case." + )] + impl<$($name: ReleaseStateQueryData),*> ReleaseStateQueryData for ($($name,)*) { + fn release_state<'w>(($($item,)*): Self::Item<'w, '_>) -> Self::Item<'w, 'static> { + ($($name::release_state($item),)*) + } + } }; } macro_rules! impl_anytuple_fetch { - ($(#[$meta:meta])* $(($name: ident, $state: ident)),*) => { + ($(#[$meta:meta])* $(($name: ident, $state: ident, $item: ident)),*) => { $(#[$meta])* #[expect( clippy::allow_attributes, @@ -2423,7 +2604,7 @@ macro_rules! impl_anytuple_fetch { } #[inline] - unsafe fn init_fetch<'w>(_world: UnsafeWorldCell<'w>, state: &Self::State, _last_run: Tick, _this_run: Tick) -> Self::Fetch<'w> { + unsafe fn init_fetch<'w, 's>(_world: UnsafeWorldCell<'w>, state: &'s Self::State, _last_run: Tick, _this_run: Tick) -> Self::Fetch<'w> { let ($($name,)*) = state; // SAFETY: The invariants are upheld by the caller. ($(( unsafe { $name::init_fetch(_world, $name, _last_run, _this_run) }, false),)*) @@ -2432,9 +2613,9 @@ macro_rules! impl_anytuple_fetch { const IS_DENSE: bool = true $(&& $name::IS_DENSE)*; #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( _fetch: &mut Self::Fetch<'w>, - _state: &Self::State, + _state: &'s Self::State, _archetype: &'w Archetype, _table: &'w Table ) { @@ -2450,7 +2631,7 @@ macro_rules! impl_anytuple_fetch { } #[inline] - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + unsafe fn set_table<'w, 's>(_fetch: &mut Self::Fetch<'w>, _state: &'s Self::State, _table: &'w Table) { let ($($name,)*) = _fetch; let ($($state,)*) = _state; $( @@ -2522,9 +2703,9 @@ macro_rules! impl_anytuple_fetch { unsafe impl<$($name: QueryData),*> QueryData for AnyOf<($($name,)*)> { const IS_READ_ONLY: bool = true $(&& $name::IS_READ_ONLY)*; type ReadOnly = AnyOf<($($name::ReadOnly,)*)>; - type Item<'w> = ($(Option<$name::Item<'w>>,)*); + type Item<'w, 's> = ($(Option<$name::Item<'w, 's>>,)*); - fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + fn shrink<'wlong: 'wshort, 'wshort, 's>(item: Self::Item<'wlong, 's>) -> Self::Item<'wshort, 's> { let ($($name,)*) = item; ($( $name.map($name::shrink), @@ -2532,15 +2713,17 @@ macro_rules! impl_anytuple_fetch { } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, _fetch: &mut Self::Fetch<'w>, _entity: Entity, _table_row: TableRow - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { let ($($name,)*) = _fetch; + let ($($state,)*) = _state; ($( // SAFETY: The invariants are required to be upheld by the caller. - $name.1.then(|| unsafe { $name::fetch(&mut $name.0, _entity, _table_row) }), + $name.1.then(|| unsafe { $name::fetch($state, &mut $name.0, _entity, _table_row) }), )*) } } @@ -2548,6 +2731,20 @@ macro_rules! impl_anytuple_fetch { $(#[$meta])* /// SAFETY: each item in the tuple is read only unsafe impl<$($name: ReadOnlyQueryData),*> ReadOnlyQueryData for AnyOf<($($name,)*)> {} + + #[expect( + clippy::allow_attributes, + reason = "This is a tuple-related macro; as such the lints below may not always apply." + )] + #[allow( + clippy::unused_unit, + reason = "Zero-length tuples will generate some function bodies equivalent to `()`; however, this macro is meant for all applicable tuples, and as such it makes no sense to rewrite it just for that case." + )] + impl<$($name: ReleaseStateQueryData),*> ReleaseStateQueryData for AnyOf<($($name,)*)> { + fn release_state<'w>(($($item,)*): Self::Item<'w, '_>) -> Self::Item<'w, 'static> { + ($($item.map(|$item| $name::release_state($item)),)*) + } + } }; } @@ -2557,7 +2754,8 @@ all_tuples!( 0, 15, F, - S + i, + s ); all_tuples!( #[doc(fake_variadic)] @@ -2565,7 +2763,8 @@ all_tuples!( 0, 15, F, - S + S, + i ); /// [`WorldQuery`] used to nullify queries by turning `Query` into `Query>` @@ -2580,7 +2779,8 @@ unsafe impl WorldQuery for NopWorldQuery { type Fetch<'w> = (); type State = D::State; - fn shrink_fetch<'wlong: 'wshort, 'wshort>(_: ()) {} + fn shrink_fetch<'wlong: 'wshort, 'wshort>(_fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> { + } #[inline(always)] unsafe fn init_fetch( @@ -2627,36 +2827,44 @@ unsafe impl WorldQuery for NopWorldQuery { unsafe impl QueryData for NopWorldQuery { const IS_READ_ONLY: bool = true; type ReadOnly = Self; - type Item<'w> = (); + type Item<'w, 's> = (); - fn shrink<'wlong: 'wshort, 'wshort>(_: ()) {} + fn shrink<'wlong: 'wshort, 'wshort, 's>( + _item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { + } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, _fetch: &mut Self::Fetch<'w>, _entity: Entity, _table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { } } /// SAFETY: `NopFetch` never accesses any data unsafe impl ReadOnlyQueryData for NopWorldQuery {} +impl ReleaseStateQueryData for NopWorldQuery { + fn release_state<'w>(_item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> {} +} + /// SAFETY: /// `update_component_access` does nothing. /// This is sound because `fetch` does not access components. unsafe impl WorldQuery for PhantomData { - type Fetch<'a> = (); + type Fetch<'w> = (); type State = (); fn shrink_fetch<'wlong: 'wshort, 'wshort>(_fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> { } - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( _world: UnsafeWorldCell<'w>, - _state: &Self::State, + _state: &'s Self::State, _last_run: Tick, _this_run: Tick, ) -> Self::Fetch<'w> { @@ -2666,15 +2874,19 @@ unsafe impl WorldQuery for PhantomData { // are stored in a Table (vacuous truth). const IS_DENSE: bool = true; - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( _fetch: &mut Self::Fetch<'w>, - _state: &Self::State, + _state: &'s Self::State, _archetype: &'w Archetype, _table: &'w Table, ) { } - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + unsafe fn set_table<'w, 's>( + _fetch: &mut Self::Fetch<'w>, + _state: &'s Self::State, + _table: &'w Table, + ) { } fn update_component_access(_state: &Self::State, _access: &mut FilteredAccess) {} @@ -2697,21 +2909,29 @@ unsafe impl WorldQuery for PhantomData { unsafe impl QueryData for PhantomData { const IS_READ_ONLY: bool = true; type ReadOnly = Self; - type Item<'a> = (); + type Item<'w, 's> = (); - fn shrink<'wlong: 'wshort, 'wshort>(_item: Self::Item<'wlong>) -> Self::Item<'wshort> {} + fn shrink<'wlong: 'wshort, 'wshort, 's>( + _item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { + } - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, _fetch: &mut Self::Fetch<'w>, _entity: Entity, _table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { } } /// SAFETY: `PhantomData` never accesses any world data. unsafe impl ReadOnlyQueryData for PhantomData {} +impl ReleaseStateQueryData for PhantomData { + fn release_state<'w>(_item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> {} +} + /// A compile-time checked union of two different types that differs based on the /// [`StorageType`] of a given component. pub(super) union StorageSwitch { @@ -2828,6 +3048,124 @@ mod tests { assert_is_system(ignored_system); } + #[test] + fn derive_release_state() { + struct NonReleaseQueryData; + + /// SAFETY: + /// `update_component_access` and `update_archetype_component_access` do nothing. + /// This is sound because `fetch` does not access components. + unsafe impl WorldQuery for NonReleaseQueryData { + type Fetch<'w> = (); + type State = (); + + fn shrink_fetch<'wlong: 'wshort, 'wshort>( + _: Self::Fetch<'wlong>, + ) -> Self::Fetch<'wshort> { + } + + unsafe fn init_fetch<'w, 's>( + _world: UnsafeWorldCell<'w>, + _state: &'s Self::State, + _last_run: Tick, + _this_run: Tick, + ) -> Self::Fetch<'w> { + } + + const IS_DENSE: bool = true; + + #[inline] + unsafe fn set_archetype<'w, 's>( + _fetch: &mut Self::Fetch<'w>, + _state: &'s Self::State, + _archetype: &'w Archetype, + _table: &Table, + ) { + } + + #[inline] + unsafe fn set_table<'w, 's>( + _fetch: &mut Self::Fetch<'w>, + _state: &'s Self::State, + _table: &'w Table, + ) { + } + + fn update_component_access( + _state: &Self::State, + _access: &mut FilteredAccess, + ) { + } + + fn init_state(_world: &mut World) {} + + fn get_state(_components: &Components) -> Option<()> { + Some(()) + } + + fn matches_component_set( + _state: &Self::State, + _set_contains_id: &impl Fn(ComponentId) -> bool, + ) -> bool { + true + } + } + + /// SAFETY: `Self` is the same as `Self::ReadOnly` + unsafe impl QueryData for NonReleaseQueryData { + type ReadOnly = Self; + const IS_READ_ONLY: bool = true; + + type Item<'w, 's> = (); + + fn shrink<'wlong: 'wshort, 'wshort, 's>( + _item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { + } + + #[inline(always)] + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, + _fetch: &mut Self::Fetch<'w>, + _entity: Entity, + _table_row: TableRow, + ) -> Self::Item<'w, 's> { + } + } + + /// SAFETY: access is read only + unsafe impl ReadOnlyQueryData for NonReleaseQueryData {} + + #[derive(QueryData)] + pub struct DerivedNonReleaseRead { + non_release: NonReleaseQueryData, + a: &'static A, + } + + #[derive(QueryData)] + #[query_data(mutable)] + pub struct DerivedNonReleaseMutable { + non_release: NonReleaseQueryData, + a: &'static mut A, + } + + #[derive(QueryData)] + pub struct DerivedReleaseRead { + a: &'static A, + } + + #[derive(QueryData)] + #[query_data(mutable)] + pub struct DerivedReleaseMutable { + a: &'static mut A, + } + + fn assert_is_release_state() {} + + assert_is_release_state::(); + assert_is_release_state::(); + } + // Ensures that each field of a `WorldQuery` struct's read-only variant // has the same visibility as its corresponding mutable field. #[test] diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index eccc819ca7..312b330e04 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -7,6 +7,7 @@ use crate::{ world::{unsafe_world_cell::UnsafeWorldCell, World}, }; use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; +use bevy_utils::prelude::DebugName; use core::{cell::UnsafeCell, marker::PhantomData}; use variadics_please::all_tuples; @@ -103,6 +104,7 @@ pub unsafe trait QueryFilter: WorldQuery { /// Must always be called _after_ [`WorldQuery::set_table`] or [`WorldQuery::set_archetype`]. `entity` and /// `table_row` must be in the range of the current table and archetype. unsafe fn filter_fetch( + state: &Self::State, fetch: &mut Self::Fetch<'_>, entity: Entity, table_row: TableRow, @@ -204,6 +206,7 @@ unsafe impl QueryFilter for With { #[inline(always)] unsafe fn filter_fetch( + _state: &Self::State, _fetch: &mut Self::Fetch<'_>, _entity: Entity, _table_row: TableRow, @@ -304,6 +307,7 @@ unsafe impl QueryFilter for Without { #[inline(always)] unsafe fn filter_fetch( + _state: &Self::State, _fetch: &mut Self::Fetch<'_>, _entity: Entity, _table_row: TableRow, @@ -400,7 +404,7 @@ macro_rules! impl_or_query_filter { const IS_DENSE: bool = true $(&& $filter::IS_DENSE)*; #[inline] - unsafe fn init_fetch<'w>(world: UnsafeWorldCell<'w>, state: &Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> { + unsafe fn init_fetch<'w, 's>(world: UnsafeWorldCell<'w>, state: &'s Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> { let ($($filter,)*) = state; ($(OrFetch { // SAFETY: The invariants are upheld by the caller. @@ -410,7 +414,7 @@ macro_rules! impl_or_query_filter { } #[inline] - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) { + unsafe fn set_table<'w, 's>(fetch: &mut Self::Fetch<'w>, state: &'s Self::State, table: &'w Table) { let ($($filter,)*) = fetch; let ($($state,)*) = state; $( @@ -423,9 +427,9 @@ macro_rules! impl_or_query_filter { } #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( fetch: &mut Self::Fetch<'w>, - state: & Self::State, + state: &'s Self::State, archetype: &'w Archetype, table: &'w Table ) { @@ -495,20 +499,22 @@ macro_rules! impl_or_query_filter { #[inline(always)] unsafe fn filter_fetch( + state: &Self::State, fetch: &mut Self::Fetch<'_>, entity: Entity, table_row: TableRow ) -> bool { + let ($($state,)*) = state; let ($($filter,)*) = fetch; // SAFETY: The invariants are upheld by the caller. - false $(|| ($filter.matches && unsafe { $filter::filter_fetch(&mut $filter.fetch, entity, table_row) }))* + false $(|| ($filter.matches && unsafe { $filter::filter_fetch($state, &mut $filter.fetch, entity, table_row) }))* } } }; } macro_rules! impl_tuple_query_filter { - ($(#[$meta:meta])* $($name: ident),*) => { + ($(#[$meta:meta])* $(($name: ident, $state: ident)),*) => { #[expect( clippy::allow_attributes, reason = "This is a tuple-related macro; as such the lints below may not always apply." @@ -528,13 +534,15 @@ macro_rules! impl_tuple_query_filter { #[inline(always)] unsafe fn filter_fetch( + state: &Self::State, fetch: &mut Self::Fetch<'_>, entity: Entity, table_row: TableRow ) -> bool { + let ($($state,)*) = state; let ($($name,)*) = fetch; // SAFETY: The invariants are upheld by the caller. - true $(&& unsafe { $name::filter_fetch($name, entity, table_row) })* + true $(&& unsafe { $name::filter_fetch($state, $name, entity, table_row) })* } } @@ -546,7 +554,8 @@ all_tuples!( impl_tuple_query_filter, 0, 15, - F + F, + S ); all_tuples!( #[doc(fake_variadic)] @@ -609,7 +618,12 @@ unsafe impl QueryFilter for Allows { const IS_ARCHETYPAL: bool = true; #[inline(always)] - unsafe fn filter_fetch(_: &mut Self::Fetch<'_>, _: Entity, _: TableRow) -> bool { + unsafe fn filter_fetch( + _: &Self::State, + _: &mut Self::Fetch<'_>, + _: Entity, + _: TableRow, + ) -> bool { true } } @@ -718,9 +732,9 @@ unsafe impl WorldQuery for Added { } #[inline] - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - &id: &ComponentId, + &id: &'s ComponentId, last_run: Tick, this_run: Tick, ) -> Self::Fetch<'w> { @@ -748,9 +762,9 @@ unsafe impl WorldQuery for Added { }; #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( fetch: &mut Self::Fetch<'w>, - component_id: &ComponentId, + component_id: &'s ComponentId, _archetype: &'w Archetype, table: &'w Table, ) { @@ -763,9 +777,9 @@ unsafe impl WorldQuery for Added { } #[inline] - unsafe fn set_table<'w>( + unsafe fn set_table<'w, 's>( fetch: &mut Self::Fetch<'w>, - &component_id: &ComponentId, + &component_id: &'s ComponentId, table: &'w Table, ) { let table_ticks = Some( @@ -781,7 +795,7 @@ unsafe impl WorldQuery for Added { #[inline] fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess) { if access.access().has_component_write(id) { - panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",core::any::type_name::()); + panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", DebugName::type_name::()); } access.add_component_read(id); } @@ -807,6 +821,7 @@ unsafe impl QueryFilter for Added { const IS_ARCHETYPAL: bool = false; #[inline(always)] unsafe fn filter_fetch( + _state: &Self::State, fetch: &mut Self::Fetch<'_>, entity: Entity, table_row: TableRow, @@ -944,9 +959,9 @@ unsafe impl WorldQuery for Changed { } #[inline] - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - &id: &ComponentId, + &id: &'s ComponentId, last_run: Tick, this_run: Tick, ) -> Self::Fetch<'w> { @@ -974,9 +989,9 @@ unsafe impl WorldQuery for Changed { }; #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( fetch: &mut Self::Fetch<'w>, - component_id: &ComponentId, + component_id: &'s ComponentId, _archetype: &'w Archetype, table: &'w Table, ) { @@ -989,9 +1004,9 @@ unsafe impl WorldQuery for Changed { } #[inline] - unsafe fn set_table<'w>( + unsafe fn set_table<'w, 's>( fetch: &mut Self::Fetch<'w>, - &component_id: &ComponentId, + &component_id: &'s ComponentId, table: &'w Table, ) { let table_ticks = Some( @@ -1007,7 +1022,7 @@ unsafe impl WorldQuery for Changed { #[inline] fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess) { if access.access().has_component_write(id) { - panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",core::any::type_name::()); + panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", DebugName::type_name::()); } access.add_component_read(id); } @@ -1034,6 +1049,7 @@ unsafe impl QueryFilter for Changed { #[inline(always)] unsafe fn filter_fetch( + _state: &Self::State, fetch: &mut Self::Fetch<'_>, entity: Entity, table_row: TableRow, @@ -1141,9 +1157,9 @@ unsafe impl WorldQuery for Spawned { } #[inline] - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - _state: &(), + _state: &'s (), last_run: Tick, this_run: Tick, ) -> Self::Fetch<'w> { @@ -1157,16 +1173,16 @@ unsafe impl WorldQuery for Spawned { const IS_DENSE: bool = true; #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( _fetch: &mut Self::Fetch<'w>, - _state: &(), + _state: &'s (), _archetype: &'w Archetype, _table: &'w Table, ) { } #[inline] - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &(), _table: &'w Table) {} + unsafe fn set_table<'w, 's>(_fetch: &mut Self::Fetch<'w>, _state: &'s (), _table: &'w Table) {} #[inline] fn update_component_access(_state: &(), _access: &mut FilteredAccess) {} @@ -1188,6 +1204,7 @@ unsafe impl QueryFilter for Spawned { #[inline(always)] unsafe fn filter_fetch( + _state: &Self::State, fetch: &mut Self::Fetch<'_>, entity: Entity, _table_row: TableRow, diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 44c2eef525..66f7ca80ad 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -140,7 +140,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { range: Option>, ) -> B where - Func: FnMut(B, D::Item<'w>) -> B, + Func: FnMut(B, D::Item<'w, 's>) -> B, { if self.cursor.is_dense { // SAFETY: `self.cursor.is_dense` is true, so storage ids are guaranteed to be table ids. @@ -203,7 +203,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { rows: Range, ) -> B where - Func: FnMut(B, D::Item<'w>) -> B, + Func: FnMut(B, D::Item<'w, 's>) -> B, { if table.is_empty() { return accum; @@ -225,14 +225,26 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { // SAFETY: set_table was called prior. // Caller assures `row` in range of the current archetype. - let fetched = unsafe { !F::filter_fetch(&mut self.cursor.filter, *entity, row) }; + let fetched = unsafe { + !F::filter_fetch( + &self.query_state.filter_state, + &mut self.cursor.filter, + *entity, + row, + ) + }; if fetched { continue; } // SAFETY: set_table was called prior. // Caller assures `row` in range of the current archetype. - let item = D::fetch(&mut self.cursor.fetch, *entity, row); + let item = D::fetch( + &self.query_state.fetch_state, + &mut self.cursor.fetch, + *entity, + row, + ); accum = func(accum, item); } @@ -255,7 +267,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { indices: Range, ) -> B where - Func: FnMut(B, D::Item<'w>) -> B, + Func: FnMut(B, D::Item<'w, 's>) -> B, { if archetype.is_empty() { return accum; @@ -283,6 +295,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { // Caller assures `index` in range of the current archetype. let fetched = unsafe { !F::filter_fetch( + &self.query_state.filter_state, &mut self.cursor.filter, archetype_entity.id(), archetype_entity.table_row(), @@ -296,6 +309,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { // Caller assures `index` in range of the current archetype. let item = unsafe { D::fetch( + &self.query_state.fetch_state, &mut self.cursor.fetch, archetype_entity.id(), archetype_entity.table_row(), @@ -324,7 +338,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { rows: Range, ) -> B where - Func: FnMut(B, D::Item<'w>) -> B, + Func: FnMut(B, D::Item<'w, 's>) -> B, { if archetype.is_empty() { return accum; @@ -356,14 +370,26 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { // SAFETY: set_table was called prior. // Caller assures `row` in range of the current archetype. - let filter_matched = unsafe { F::filter_fetch(&mut self.cursor.filter, entity, row) }; + let filter_matched = unsafe { + F::filter_fetch( + &self.query_state.filter_state, + &mut self.cursor.filter, + entity, + row, + ) + }; if !filter_matched { continue; } // SAFETY: set_table was called prior. // Caller assures `row` in range of the current archetype. - let item = D::fetch(&mut self.cursor.fetch, entity, row); + let item = D::fetch( + &self.query_state.fetch_state, + &mut self.cursor.fetch, + entity, + row, + ); accum = func(accum, item); } @@ -492,7 +518,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > where - for<'lw> L::Item<'lw>: Ord, + for<'lw, 'ls> L::Item<'lw, 'ls>: Ord, { self.sort_impl::(|keyed_query| keyed_query.sort()) } @@ -549,7 +575,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > where - for<'lw> L::Item<'lw>: Ord, + for<'lw, 'ls> L::Item<'lw, 'ls>: Ord, { self.sort_impl::(|keyed_query| keyed_query.sort_unstable()) } @@ -605,7 +631,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// ``` pub fn sort_by( self, - mut compare: impl FnMut(&L::Item<'_>, &L::Item<'_>) -> Ordering, + mut compare: impl FnMut(&L::Item<'_, '_>, &L::Item<'_, '_>) -> Ordering, ) -> QuerySortedIter< 'w, 's, @@ -637,7 +663,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// This will panic if `next` has been called on `QueryIter` before, unless the underlying `Query` is empty. pub fn sort_unstable_by( self, - mut compare: impl FnMut(&L::Item<'_>, &L::Item<'_>) -> Ordering, + mut compare: impl FnMut(&L::Item<'_, '_>, &L::Item<'_, '_>) -> Ordering, ) -> QuerySortedIter< 'w, 's, @@ -729,7 +755,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// ``` pub fn sort_by_key( self, - mut f: impl FnMut(&L::Item<'_>) -> K, + mut f: impl FnMut(&L::Item<'_, '_>) -> K, ) -> QuerySortedIter< 'w, 's, @@ -762,7 +788,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// This will panic if `next` has been called on `QueryIter` before, unless the underlying `Query` is empty. pub fn sort_unstable_by_key( self, - mut f: impl FnMut(&L::Item<'_>) -> K, + mut f: impl FnMut(&L::Item<'_, '_>) -> K, ) -> QuerySortedIter< 'w, 's, @@ -797,7 +823,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// This will panic if `next` has been called on `QueryIter` before, unless the underlying `Query` is empty. pub fn sort_by_cached_key( self, - mut f: impl FnMut(&L::Item<'_>) -> K, + mut f: impl FnMut(&L::Item<'_, '_>) -> K, ) -> QuerySortedIter< 'w, 's, @@ -827,7 +853,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// This will panic if `next` has been called on `QueryIter` before, unless the underlying `Query` is empty. fn sort_impl( self, - f: impl FnOnce(&mut Vec<(L::Item<'_>, NeutralOrd)>), + f: impl FnOnce(&mut Vec<(L::Item<'_, '_>, NeutralOrd)>), ) -> QuerySortedIter< 'w, 's, @@ -856,7 +882,11 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { .map(|(key, entity)| (key, NeutralOrd(entity))) .collect(); f(&mut keyed_query); - let entity_iter = keyed_query.into_iter().map(|(.., entity)| entity.0); + let entity_iter = keyed_query + .into_iter() + .map(|(.., entity)| entity.0) + .collect::>() + .into_iter(); // SAFETY: // `self.world` has permission to access the required components. // Each lens query item is dropped before the respective actual query item is accessed. @@ -873,7 +903,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { } impl<'w, 's, D: QueryData, F: QueryFilter> Iterator for QueryIter<'w, 's, D, F> { - type Item = D::Item<'w>; + type Item = D::Item<'w, 's>; #[inline(always)] fn next(&mut self) -> Option { @@ -1010,7 +1040,7 @@ where /// # Safety /// `entity` must stem from `self.entity_iter`, and not have been passed before. #[inline(always)] - unsafe fn fetch_next(&mut self, entity: Entity) -> D::Item<'w> { + unsafe fn fetch_next(&mut self, entity: Entity) -> D::Item<'w, 's> { let (location, archetype, table); // SAFETY: // `tables` and `archetypes` belong to the same world that the [`QueryIter`] @@ -1039,7 +1069,14 @@ where // SAFETY: // - set_archetype was called prior, `location.archetype_row` is an archetype index in range of the current archetype // - fetch is only called once for each entity. - unsafe { D::fetch(&mut self.fetch, entity, location.table_row) } + unsafe { + D::fetch( + &self.query_state.fetch_state, + &mut self.fetch, + entity, + location.table_row, + ) + } } } @@ -1048,7 +1085,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> Iterator where I: Iterator, { - type Item = D::Item<'w>; + type Item = D::Item<'w, 's>; #[inline(always)] fn next(&mut self) -> Option { @@ -1170,7 +1207,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> fetch: &mut D::Fetch<'w>, filter: &mut F::Fetch<'w>, query_state: &'s QueryState, - ) -> Option> { + ) -> Option> { for entity_borrow in entity_iter { let entity = entity_borrow.entity(); let Ok(location) = entities.get_constructed(entity) else { @@ -1200,11 +1237,20 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> // SAFETY: set_archetype was called prior. // `location.archetype_row` is an archetype index row in range of the current archetype, because if it was not, the match above would have `continue`d - if unsafe { F::filter_fetch(filter, entity, location.table_row) } { + if unsafe { + F::filter_fetch( + &query_state.filter_state, + filter, + entity, + location.table_row, + ) + } { // SAFETY: // - set_archetype was called prior, `location.archetype_row` is an archetype index in range of the current archetype // - fetch is only called once for each entity. - return Some(unsafe { D::fetch(fetch, entity, location.table_row) }); + return Some(unsafe { + D::fetch(&query_state.fetch_state, fetch, entity, location.table_row) + }); } } None @@ -1212,7 +1258,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// Get next result from the query #[inline(always)] - pub fn fetch_next(&mut self) -> Option> { + pub fn fetch_next(&mut self) -> Option> { // SAFETY: // All arguments stem from self. // We are limiting the returned reference to self, @@ -1336,7 +1382,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > where - for<'lw> L::Item<'lw>: Ord, + for<'lw, 'ls> L::Item<'lw, 'ls>: Ord, { self.sort_impl::(|keyed_query| keyed_query.sort()) } @@ -1394,7 +1440,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > where - for<'lw> L::Item<'lw>: Ord, + for<'lw, 'ls> L::Item<'lw, 'ls>: Ord, { self.sort_impl::(|keyed_query| keyed_query.sort_unstable()) } @@ -1451,7 +1497,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// ``` pub fn sort_by( self, - mut compare: impl FnMut(&L::Item<'_>, &L::Item<'_>) -> Ordering, + mut compare: impl FnMut(&L::Item<'_, '_>, &L::Item<'_, '_>) -> Ordering, ) -> QuerySortedManyIter< 'w, 's, @@ -1482,7 +1528,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// called on [`QueryManyIter`] before. pub fn sort_unstable_by( self, - mut compare: impl FnMut(&L::Item<'_>, &L::Item<'_>) -> Ordering, + mut compare: impl FnMut(&L::Item<'_, '_>, &L::Item<'_, '_>) -> Ordering, ) -> QuerySortedManyIter< 'w, 's, @@ -1576,7 +1622,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// ``` pub fn sort_by_key( self, - mut f: impl FnMut(&L::Item<'_>) -> K, + mut f: impl FnMut(&L::Item<'_, '_>) -> K, ) -> QuerySortedManyIter< 'w, 's, @@ -1608,7 +1654,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// called on [`QueryManyIter`] before. pub fn sort_unstable_by_key( self, - mut f: impl FnMut(&L::Item<'_>) -> K, + mut f: impl FnMut(&L::Item<'_, '_>) -> K, ) -> QuerySortedManyIter< 'w, 's, @@ -1642,7 +1688,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// called on [`QueryManyIter`] before. pub fn sort_by_cached_key( self, - mut f: impl FnMut(&L::Item<'_>) -> K, + mut f: impl FnMut(&L::Item<'_, '_>) -> K, ) -> QuerySortedManyIter< 'w, 's, @@ -1671,7 +1717,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// called on [`QueryManyIter`] before. fn sort_impl( self, - f: impl FnOnce(&mut Vec<(L::Item<'_>, NeutralOrd)>), + f: impl FnOnce(&mut Vec<(L::Item<'_, '_>, NeutralOrd)>), ) -> QuerySortedManyIter< 'w, 's, @@ -1721,7 +1767,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator Option> { + pub fn fetch_next_back(&mut self) -> Option> { // SAFETY: // All arguments stem from self. // We are limiting the returned reference to self, @@ -1745,7 +1791,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator> Iterator for QueryManyIter<'w, 's, D, F, I> { - type Item = D::Item<'w>; + type Item = D::Item<'w, 's>; #[inline(always)] fn next(&mut self) -> Option { @@ -1861,7 +1907,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator> impl<'w, 's, D: QueryData, F: QueryFilter, I: EntitySetIterator> Iterator for QueryManyUniqueIter<'w, 's, D, F, I> { - type Item = D::Item<'w>; + type Item = D::Item<'w, 's>; #[inline(always)] fn next(&mut self) -> Option { @@ -1954,7 +2000,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// It is always safe for shared access. /// `entity` must stem from `self.entity_iter`, and not have been passed before. #[inline(always)] - unsafe fn fetch_next_aliased_unchecked(&mut self, entity: Entity) -> D::Item<'w> { + unsafe fn fetch_next_aliased_unchecked(&mut self, entity: Entity) -> D::Item<'w, 's> { let (location, archetype, table); // SAFETY: // `tables` and `archetypes` belong to the same world that the [`QueryIter`] @@ -1983,12 +2029,19 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> // SAFETY: // - set_archetype was called prior, `location.archetype_row` is an archetype index in range of the current archetype // - fetch is only called once for each entity. - unsafe { D::fetch(&mut self.fetch, entity, location.table_row) } + unsafe { + D::fetch( + &self.query_state.fetch_state, + &mut self.fetch, + entity, + location.table_row, + ) + } } /// Get next result from the query #[inline(always)] - pub fn fetch_next(&mut self) -> Option> { + pub fn fetch_next(&mut self) -> Option> { let entity = self.entity_iter.next()?; // SAFETY: @@ -2007,7 +2060,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator { /// Get next result from the query #[inline(always)] - pub fn fetch_next_back(&mut self) -> Option> { + pub fn fetch_next_back(&mut self) -> Option> { let entity = self.entity_iter.next_back()?; // SAFETY: @@ -2024,7 +2077,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: DoubleEndedIterator impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, I: Iterator> Iterator for QuerySortedManyIter<'w, 's, D, F, I> { - type Item = D::Item<'w>; + type Item = D::Item<'w, 's>; #[inline(always)] fn next(&mut self) -> Option { @@ -2185,7 +2238,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> QueryCombinationIter< /// . /// It is always safe for shared access. #[inline] - unsafe fn fetch_next_aliased_unchecked(&mut self) -> Option<[D::Item<'w>; K]> { + unsafe fn fetch_next_aliased_unchecked(&mut self) -> Option<[D::Item<'w, 's>; K]> { // PERF: can speed up the following code using `cursor.remaining()` instead of `next_item.is_none()` // when D::IS_ARCHETYPAL && F::IS_ARCHETYPAL // @@ -2211,11 +2264,12 @@ impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> QueryCombinationIter< } } - let mut values = MaybeUninit::<[D::Item<'w>; K]>::uninit(); + let mut values = MaybeUninit::<[D::Item<'w, 's>; K]>::uninit(); - let ptr = values.as_mut_ptr().cast::>(); + let ptr = values.as_mut_ptr().cast::>(); for (offset, cursor) in self.cursors.iter_mut().enumerate() { - ptr.add(offset).write(cursor.peek_last().unwrap()); + ptr.add(offset) + .write(cursor.peek_last(self.query_state).unwrap()); } Some(values.assume_init()) @@ -2223,7 +2277,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> QueryCombinationIter< /// Get next combination of queried components #[inline] - pub fn fetch_next(&mut self) -> Option<[D::Item<'_>; K]> { + pub fn fetch_next(&mut self) -> Option<[D::Item<'_, 's>; K]> { // SAFETY: we are limiting the returned reference to self, // making sure this method cannot be called multiple times without getting rid // of any previously returned unique references first, thus preventing aliasing. @@ -2240,7 +2294,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> QueryCombinationIter< impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, const K: usize> Iterator for QueryCombinationIter<'w, 's, D, F, K> { - type Item = [D::Item<'w>; K]; + type Item = [D::Item<'w, 's>; K]; #[inline] fn next(&mut self) -> Option { @@ -2390,7 +2444,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { /// The result of `next` and any previous calls to `peek_last` with this row must have been /// dropped to prevent aliasing mutable references. #[inline] - unsafe fn peek_last(&mut self) -> Option> { + unsafe fn peek_last(&mut self, query_state: &'s QueryState) -> Option> { if self.current_row > 0 { let index = self.current_row - 1; if self.is_dense { @@ -2401,6 +2455,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { // - `*entity` and `index` are in the current table. unsafe { Some(D::fetch( + &query_state.fetch_state, &mut self.fetch, *entity, // SAFETY: This is from an exclusive range, so it can't be max. @@ -2416,6 +2471,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { // - `archetype_entity.id()` and `archetype_entity.table_row()` are in the current archetype. unsafe { Some(D::fetch( + &query_state.fetch_state, &mut self.fetch, archetype_entity.id(), archetype_entity.table_row(), @@ -2457,7 +2513,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { tables: &'w Tables, archetypes: &'w Archetypes, query_state: &'s QueryState, - ) -> Option> { + ) -> Option> { if self.is_dense { loop { // we are on the beginning of the query, or finished processing a table, so skip to the next @@ -2484,7 +2540,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { unsafe { self.table_entities.get_unchecked(self.current_row as usize) }; // SAFETY: The row is less than the u32 len, so it must not be max. let row = unsafe { TableRow::new(NonMaxU32::new_unchecked(self.current_row)) }; - if !F::filter_fetch(&mut self.filter, *entity, row) { + if !F::filter_fetch(&query_state.filter_state, &mut self.filter, *entity, row) { self.current_row += 1; continue; } @@ -2494,7 +2550,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { // - `current_row` must be a table row in range of the current table, // because if it was not, then the above would have been executed. // - fetch is only called once for each `entity`. - let item = unsafe { D::fetch(&mut self.fetch, *entity, row) }; + let item = + unsafe { D::fetch(&query_state.fetch_state, &mut self.fetch, *entity, row) }; self.current_row += 1; return Some(item); @@ -2536,6 +2593,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { .get_unchecked(self.current_row as usize) }; if !F::filter_fetch( + &query_state.filter_state, &mut self.filter, archetype_entity.id(), archetype_entity.table_row(), @@ -2551,6 +2609,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { // - fetch is only called once for each `archetype_entity`. let item = unsafe { D::fetch( + &query_state.fetch_state, &mut self.fetch, archetype_entity.id(), archetype_entity.table_row(), diff --git a/crates/bevy_ecs/src/query/mod.rs b/crates/bevy_ecs/src/query/mod.rs index eb5e001e4d..7c1487fde4 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -824,9 +824,9 @@ mod tests { fn shrink_fetch<'wlong: 'wshort, 'wshort>(_: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {} - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( _world: UnsafeWorldCell<'w>, - _state: &Self::State, + _state: &'s Self::State, _last_run: Tick, _this_run: Tick, ) -> Self::Fetch<'w> { @@ -835,18 +835,18 @@ mod tests { const IS_DENSE: bool = true; #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( _fetch: &mut Self::Fetch<'w>, - _state: &Self::State, + _state: &'s Self::State, _archetype: &'w Archetype, _table: &Table, ) { } #[inline] - unsafe fn set_table<'w>( + unsafe fn set_table<'w, 's>( _fetch: &mut Self::Fetch<'w>, - _state: &Self::State, + _state: &'s Self::State, _table: &'w Table, ) { } @@ -882,16 +882,20 @@ mod tests { unsafe impl QueryData for ReadsRData { const IS_READ_ONLY: bool = true; type ReadOnly = Self; - type Item<'w> = (); + type Item<'w, 's> = (); - fn shrink<'wlong: 'wshort, 'wshort>(_item: Self::Item<'wlong>) -> Self::Item<'wshort> {} + fn shrink<'wlong: 'wshort, 'wshort, 's>( + _item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { + } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + _state: &'s Self::State, _fetch: &mut Self::Fetch<'w>, _entity: Entity, _table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { } } diff --git a/crates/bevy_ecs/src/query/par_iter.rs b/crates/bevy_ecs/src/query/par_iter.rs index c685659260..b8d8618fa5 100644 --- a/crates/bevy_ecs/src/query/par_iter.rs +++ b/crates/bevy_ecs/src/query/par_iter.rs @@ -39,7 +39,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryParIter<'w, 's, D, F> { /// /// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool #[inline] - pub fn for_each) + Send + Sync + Clone>(self, func: FN) { + pub fn for_each) + Send + Sync + Clone>(self, func: FN) { self.for_each_init(|| {}, |_, item| func(item)); } @@ -76,7 +76,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryParIter<'w, 's, D, F> { #[inline] pub fn for_each_init(self, init: INIT, func: FN) where - FN: Fn(&mut T, QueryItem<'w, D>) + Send + Sync + Clone, + FN: Fn(&mut T, QueryItem<'w, 's, D>) + Send + Sync + Clone, INIT: Fn() -> T + Sync + Send + Clone, { let func = |mut init, item| { @@ -190,7 +190,7 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, E: EntityEquivalent + Sync> /// /// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool #[inline] - pub fn for_each) + Send + Sync + Clone>(self, func: FN) { + pub fn for_each) + Send + Sync + Clone>(self, func: FN) { self.for_each_init(|| {}, |_, item| func(item)); } @@ -247,7 +247,7 @@ impl<'w, 's, D: ReadOnlyQueryData, F: QueryFilter, E: EntityEquivalent + Sync> #[inline] pub fn for_each_init(self, init: INIT, func: FN) where - FN: Fn(&mut T, QueryItem<'w, D>) + Send + Sync + Clone, + FN: Fn(&mut T, QueryItem<'w, 's, D>) + Send + Sync + Clone, INIT: Fn() -> T + Sync + Send + Clone, { let func = |mut init, item| { @@ -345,7 +345,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, E: EntityEquivalent + Sync> /// /// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool #[inline] - pub fn for_each) + Send + Sync + Clone>(self, func: FN) { + pub fn for_each) + Send + Sync + Clone>(self, func: FN) { self.for_each_init(|| {}, |_, item| func(item)); } @@ -402,7 +402,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, E: EntityEquivalent + Sync> #[inline] pub fn for_each_init(self, init: INIT, func: FN) where - FN: Fn(&mut T, QueryItem<'w, D>) + Send + Sync + Clone, + FN: Fn(&mut T, QueryItem<'w, 's, D>) + Send + Sync + Clone, INIT: Fn() -> T + Sync + Send + Clone, { let func = |mut init, item| { diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 9e34887abf..63df9b4d8d 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -14,6 +14,7 @@ use crate::{ use crate::entity::UniqueEntityEquivalentSlice; use alloc::vec::Vec; +use bevy_utils::prelude::DebugName; use core::{fmt, ptr}; use fixedbitset::FixedBitSet; use log::warn; @@ -672,7 +673,7 @@ impl QueryState { assert!( component_access.is_subset(&self_access), "Transmuted state for {} attempts to access terms that are not allowed by original state {}.", - core::any::type_name::<(NewD, NewF)>(), core::any::type_name::<(D, F)>() + DebugName::type_name::<(NewD, NewF)>(), DebugName::type_name::<(D, F)>() ); QueryState { @@ -791,7 +792,7 @@ impl QueryState { assert!( component_access.is_subset(&joined_component_access), "Joined state for {} attempts to access terms that are not allowed by state {} joined with {}.", - core::any::type_name::<(NewD, NewF)>(), core::any::type_name::<(D, F)>(), core::any::type_name::<(OtherD, OtherF)>() + DebugName::type_name::<(NewD, NewF)>(), DebugName::type_name::<(D, F)>(), DebugName::type_name::<(OtherD, OtherF)>() ); if self.archetype_generation != other.archetype_generation { @@ -845,13 +846,17 @@ impl QueryState { /// /// This can only be called for read-only queries, see [`Self::get_mut`] for write-queries. /// + /// If you need to get multiple items at once but get borrowing errors, + /// consider using [`Self::update_archetypes`] followed by multiple [`Self::get_manual`] calls, + /// or making a single call with [`Self::get_many`] or [`Self::iter_many`]. + /// /// This is always guaranteed to run in `O(1)` time. #[inline] pub fn get<'w>( &mut self, world: &'w World, entity: Entity, - ) -> Result, QueryEntityError> { + ) -> Result, QueryEntityError> { self.query(world).get_inner(entity) } @@ -892,7 +897,7 @@ impl QueryState { &mut self, world: &'w World, entities: [Entity; N], - ) -> Result<[ROQueryItem<'w, D>; N], QueryEntityError> { + ) -> Result<[ROQueryItem<'w, '_, D>; N], QueryEntityError> { self.query(world).get_many_inner(entities) } @@ -930,7 +935,7 @@ impl QueryState { &mut self, world: &'w World, entities: UniqueEntityArray, - ) -> Result<[ROQueryItem<'w, D>; N], QueryEntityError> { + ) -> Result<[ROQueryItem<'w, '_, D>; N], QueryEntityError> { self.query(world).get_many_unique_inner(entities) } @@ -942,7 +947,7 @@ impl QueryState { &mut self, world: &'w mut World, entity: Entity, - ) -> Result, QueryEntityError> { + ) -> Result, QueryEntityError> { self.query_mut(world).get_inner(entity) } @@ -989,7 +994,7 @@ impl QueryState { &mut self, world: &'w mut World, entities: [Entity; N], - ) -> Result<[D::Item<'w>; N], QueryEntityError> { + ) -> Result<[D::Item<'w, '_>; N], QueryEntityError> { self.query_mut(world).get_many_mut_inner(entities) } @@ -1034,7 +1039,7 @@ impl QueryState { &mut self, world: &'w mut World, entities: UniqueEntityArray, - ) -> Result<[D::Item<'w>; N], QueryEntityError> { + ) -> Result<[D::Item<'w, '_>; N], QueryEntityError> { self.query_mut(world).get_many_unique_inner(entities) } @@ -1056,7 +1061,7 @@ impl QueryState { &self, world: &'w World, entity: Entity, - ) -> Result, QueryEntityError> { + ) -> Result, QueryEntityError> { self.query_manual(world).get_inner(entity) } @@ -1073,13 +1078,16 @@ impl QueryState { &mut self, world: UnsafeWorldCell<'w>, entity: Entity, - ) -> Result, QueryEntityError> { + ) -> Result, QueryEntityError> { self.query_unchecked(world).get_inner(entity) } /// Returns an [`Iterator`] over the query results for the given [`World`]. /// /// This can only be called for read-only queries, see [`Self::iter_mut`] for write-queries. + /// + /// If you need to iterate multiple times at once but get borrowing errors, + /// consider using [`Self::update_archetypes`] followed by multiple [`Self::iter_manual`] calls. #[inline] pub fn iter<'w, 's>(&'s mut self, world: &'w World) -> QueryIter<'w, 's, D::ReadOnly, F> { self.query(world).into_iter() @@ -1168,6 +1176,9 @@ impl QueryState { /// Items are returned in the order of the list of entities. /// Entities that don't match the query are skipped. /// + /// If you need to iterate multiple times at once but get borrowing errors, + /// consider using [`Self::update_archetypes`] followed by multiple [`Self::iter_many_manual`] calls. + /// /// # See also /// /// - [`iter_many_mut`](Self::iter_many_mut) to get mutable query items. @@ -1387,8 +1398,8 @@ impl QueryState { /// /// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool #[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))] - pub(crate) unsafe fn par_fold_init_unchecked_manual<'w, T, FN, INIT>( - &self, + pub(crate) unsafe fn par_fold_init_unchecked_manual<'w, 's, T, FN, INIT>( + &'s self, init_accum: INIT, world: UnsafeWorldCell<'w>, batch_size: u32, @@ -1396,7 +1407,7 @@ impl QueryState { last_run: Tick, this_run: Tick, ) where - FN: Fn(T, D::Item<'w>) -> T + Send + Sync + Clone, + FN: Fn(T, D::Item<'w, 's>) -> T + Send + Sync + Clone, INIT: Fn() -> T + Sync + Send + Clone, { // NOTE: If you are changing query iteration code, remember to update the following places, where relevant: @@ -1501,8 +1512,8 @@ impl QueryState { /// /// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool #[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))] - pub(crate) unsafe fn par_many_unique_fold_init_unchecked_manual<'w, T, FN, INIT, E>( - &self, + pub(crate) unsafe fn par_many_unique_fold_init_unchecked_manual<'w, 's, T, FN, INIT, E>( + &'s self, init_accum: INIT, world: UnsafeWorldCell<'w>, entity_list: &UniqueEntityEquivalentSlice, @@ -1511,7 +1522,7 @@ impl QueryState { last_run: Tick, this_run: Tick, ) where - FN: Fn(T, D::Item<'w>) -> T + Send + Sync + Clone, + FN: Fn(T, D::Item<'w, 's>) -> T + Send + Sync + Clone, INIT: Fn() -> T + Sync + Send + Clone, E: EntityEquivalent + Sync, { @@ -1564,8 +1575,8 @@ impl QueryState { /// /// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool #[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))] - pub(crate) unsafe fn par_many_fold_init_unchecked_manual<'w, T, FN, INIT, E>( - &self, + pub(crate) unsafe fn par_many_fold_init_unchecked_manual<'w, 's, T, FN, INIT, E>( + &'s self, init_accum: INIT, world: UnsafeWorldCell<'w>, entity_list: &[E], @@ -1574,7 +1585,7 @@ impl QueryState { last_run: Tick, this_run: Tick, ) where - FN: Fn(T, D::Item<'w>) -> T + Send + Sync + Clone, + FN: Fn(T, D::Item<'w, 's>) -> T + Send + Sync + Clone, INIT: Fn() -> T + Sync + Send + Clone, E: EntityEquivalent + Sync, { @@ -1686,7 +1697,10 @@ impl QueryState { /// /// Simply unwrapping the [`Result`] also works, but should generally be reserved for tests. #[inline] - pub fn single<'w>(&mut self, world: &'w World) -> Result, QuerySingleError> { + pub fn single<'w>( + &mut self, + world: &'w World, + ) -> Result, QuerySingleError> { self.query(world).single_inner() } @@ -1703,7 +1717,7 @@ impl QueryState { pub fn single_mut<'w>( &mut self, world: &'w mut World, - ) -> Result, QuerySingleError> { + ) -> Result, QuerySingleError> { self.query_mut(world).single_inner() } @@ -1720,7 +1734,7 @@ impl QueryState { pub unsafe fn single_unchecked<'w>( &mut self, world: UnsafeWorldCell<'w>, - ) -> Result, QuerySingleError> { + ) -> Result, QuerySingleError> { self.query_unchecked(world).single_inner() } @@ -1742,7 +1756,7 @@ impl QueryState { world: UnsafeWorldCell<'w>, last_run: Tick, this_run: Tick, - ) -> Result, QuerySingleError> { + ) -> Result, QuerySingleError> { // SAFETY: // - The caller ensured we have the correct access to the world. // - The caller ensured that the world matches. diff --git a/crates/bevy_ecs/src/query/world_query.rs b/crates/bevy_ecs/src/query/world_query.rs index a6bcbf58bd..1c739927ac 100644 --- a/crates/bevy_ecs/src/query/world_query.rs +++ b/crates/bevy_ecs/src/query/world_query.rs @@ -42,7 +42,7 @@ use variadics_please::all_tuples; /// [`QueryFilter`]: crate::query::QueryFilter pub unsafe trait WorldQuery { /// Per archetype/table state retrieved by this [`WorldQuery`] to compute [`Self::Item`](crate::query::QueryData::Item) for each entity. - type Fetch<'a>: Clone; + type Fetch<'w>: Clone; /// State used to construct a [`Self::Fetch`](WorldQuery::Fetch). This will be cached inside [`QueryState`](crate::query::QueryState), /// so it is best to move as much data / computation here as possible to reduce the cost of @@ -62,9 +62,9 @@ pub unsafe trait WorldQuery { /// in to this function. /// - `world` must have the **right** to access any access registered in `update_component_access`. /// - There must not be simultaneous resource access conflicting with readonly resource access registered in [`WorldQuery::update_component_access`]. - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - state: &Self::State, + state: &'s Self::State, last_run: Tick, this_run: Tick, ) -> Self::Fetch<'w>; @@ -87,9 +87,9 @@ pub unsafe trait WorldQuery { /// - `archetype` and `tables` must be from the same [`World`] that [`WorldQuery::init_state`] was called on. /// - `table` must correspond to `archetype`. /// - `state` must be the [`State`](Self::State) that `fetch` was initialized with. - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( fetch: &mut Self::Fetch<'w>, - state: &Self::State, + state: &'s Self::State, archetype: &'w Archetype, table: &'w Table, ); @@ -101,7 +101,11 @@ pub unsafe trait WorldQuery { /// /// - `table` must be from the same [`World`] that [`WorldQuery::init_state`] was called on. /// - `state` must be the [`State`](Self::State) that `fetch` was initialized with. - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table); + unsafe fn set_table<'w, 's>( + fetch: &mut Self::Fetch<'w>, + state: &'s Self::State, + table: &'w Table, + ); /// Adds any component accesses used by this [`WorldQuery`] to `access`. /// @@ -166,7 +170,7 @@ macro_rules! impl_tuple_world_query { } #[inline] - unsafe fn init_fetch<'w>(world: UnsafeWorldCell<'w>, state: &Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> { + unsafe fn init_fetch<'w, 's>(world: UnsafeWorldCell<'w>, state: &'s Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> { let ($($name,)*) = state; // SAFETY: The invariants are upheld by the caller. ($(unsafe { $name::init_fetch(world, $name, last_run, this_run) },)*) @@ -175,9 +179,9 @@ macro_rules! impl_tuple_world_query { const IS_DENSE: bool = true $(&& $name::IS_DENSE)*; #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( fetch: &mut Self::Fetch<'w>, - state: &Self::State, + state: &'s Self::State, archetype: &'w Archetype, table: &'w Table ) { @@ -188,7 +192,7 @@ macro_rules! impl_tuple_world_query { } #[inline] - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) { + unsafe fn set_table<'w, 's>(fetch: &mut Self::Fetch<'w>, state: &'s Self::State, table: &'w Table) { let ($($name,)*) = fetch; let ($($state,)*) = state; // SAFETY: The invariants are upheld by the caller. diff --git a/crates/bevy_ecs/src/reflect/bundle.rs b/crates/bevy_ecs/src/reflect/bundle.rs index ee02aff86e..133591c405 100644 --- a/crates/bevy_ecs/src/reflect/bundle.rs +++ b/crates/bevy_ecs/src/reflect/bundle.rs @@ -5,6 +5,7 @@ //! //! Same as [`super::component`], but for bundles. use alloc::boxed::Box; +use bevy_utils::prelude::DebugName; use core::any::{Any, TypeId}; use crate::{ @@ -172,7 +173,7 @@ impl FromType for Refl _ => panic!( "expected bundle `{}` to be named struct or tuple", // FIXME: once we have unique reflect, use `TypePath`. - core::any::type_name::(), + DebugName::type_name::(), ), } } @@ -215,7 +216,7 @@ impl FromType for Refl _ => panic!( "expected bundle `{}` to be a named struct or tuple", // FIXME: once we have unique reflect, use `TypePath`. - core::any::type_name::(), + DebugName::type_name::(), ), } } diff --git a/crates/bevy_ecs/src/reflect/component.rs b/crates/bevy_ecs/src/reflect/component.rs index 893e9b13fa..8a5c17179e 100644 --- a/crates/bevy_ecs/src/reflect/component.rs +++ b/crates/bevy_ecs/src/reflect/component.rs @@ -70,7 +70,7 @@ use crate::{ }, }; use bevy_reflect::{FromReflect, FromType, PartialReflect, Reflect, TypePath, TypeRegistry}; -use disqualified::ShortName; +use bevy_utils::prelude::DebugName; /// A struct used to operate on reflected [`Component`] trait of a type. /// @@ -308,7 +308,8 @@ impl FromType for ReflectComponent { }, apply: |mut entity, reflected_component| { if !C::Mutability::MUTABLE { - let name = ShortName::of::(); + let name = DebugName::type_name::(); + let name = name.shortname(); panic!("Cannot call `ReflectComponent::apply` on component {name}. It is immutable, and cannot modified through reflection"); } @@ -357,7 +358,8 @@ impl FromType for ReflectComponent { reflect: |entity| entity.get::().map(|c| c as &dyn Reflect), reflect_mut: |entity| { if !C::Mutability::MUTABLE { - let name = ShortName::of::(); + let name = DebugName::type_name::(); + let name = name.shortname(); panic!("Cannot call `ReflectComponent::reflect_mut` on component {name}. It is immutable, and cannot modified through reflection"); } @@ -370,7 +372,8 @@ impl FromType for ReflectComponent { }, reflect_unchecked_mut: |entity| { if !C::Mutability::MUTABLE { - let name = ShortName::of::(); + let name = DebugName::type_name::(); + let name = name.shortname(); panic!("Cannot call `ReflectComponent::reflect_unchecked_mut` on component {name}. It is immutable, and cannot modified through reflection"); } diff --git a/crates/bevy_ecs/src/reflect/mod.rs b/crates/bevy_ecs/src/reflect/mod.rs index b630f58719..24e1449e61 100644 --- a/crates/bevy_ecs/src/reflect/mod.rs +++ b/crates/bevy_ecs/src/reflect/mod.rs @@ -18,6 +18,7 @@ mod from_world; mod map_entities; mod resource; +use bevy_utils::prelude::DebugName; pub use bundle::{ReflectBundle, ReflectBundleFns}; pub use component::{ReflectComponent, ReflectComponentFns}; pub use entity_commands::ReflectCommandExt; @@ -136,7 +137,7 @@ pub fn from_reflect_with_fallback( `Default` or `FromWorld` traits. Are you perhaps missing a `#[reflect(Default)]` \ or `#[reflect(FromWorld)]`?", // FIXME: once we have unique reflect, use `TypePath`. - core::any::type_name::(), + DebugName::type_name::(), ); }; diff --git a/crates/bevy_ecs/src/relationship/mod.rs b/crates/bevy_ecs/src/relationship/mod.rs index 00334cb6d0..d570b9fabc 100644 --- a/crates/bevy_ecs/src/relationship/mod.rs +++ b/crates/bevy_ecs/src/relationship/mod.rs @@ -6,6 +6,7 @@ mod relationship_source_collection; use alloc::format; +use bevy_utils::prelude::DebugName; pub use related_methods::*; pub use relationship_query::*; pub use relationship_source_collection::*; @@ -105,8 +106,8 @@ pub trait Relationship: Component + Sized { warn!( "{}The {}({target_entity:?}) relationship on entity {entity:?} points to itself. The invalid {} relationship has been removed.", caller.map(|location|format!("{location}: ")).unwrap_or_default(), - core::any::type_name::(), - core::any::type_name::() + DebugName::type_name::(), + DebugName::type_name::() ); world.commands().entity(entity).remove::(); return; @@ -125,8 +126,8 @@ pub trait Relationship: Component + Sized { warn!( "{}The {}({target_entity:?}) relationship on entity {entity:?} relates to an entity that does not exist. The invalid {} relationship has been removed.", caller.map(|location|format!("{location}: ")).unwrap_or_default(), - core::any::type_name::(), - core::any::type_name::() + DebugName::type_name::(), + DebugName::type_name::() ); world.commands().entity(entity).remove::(); } diff --git a/crates/bevy_ecs/src/relationship/relationship_query.rs b/crates/bevy_ecs/src/relationship/relationship_query.rs index a2ec937c29..a7acea7de0 100644 --- a/crates/bevy_ecs/src/relationship/relationship_query.rs +++ b/crates/bevy_ecs/src/relationship/relationship_query.rs @@ -14,7 +14,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// target entity of that relationship. pub fn related(&'w self, entity: Entity) -> Option where - ::ReadOnly: QueryData = &'w R>, + ::ReadOnly: QueryData = &'w R>, { self.get(entity).map(R::get).ok() } @@ -26,7 +26,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { entity: Entity, ) -> impl Iterator + 'w where - ::ReadOnly: QueryData = &'w S>, + ::ReadOnly: QueryData = &'w S>, { self.get(entity) .into_iter() @@ -42,7 +42,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// If your relationship is not a tree (like Bevy's hierarchy), be sure to stop if you encounter a duplicate entity. pub fn root_ancestor(&'w self, entity: Entity) -> Entity where - ::ReadOnly: QueryData = &'w R>, + ::ReadOnly: QueryData = &'w R>, { // Recursively search up the tree until we're out of parents match self.get(entity) { @@ -60,9 +60,9 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn iter_leaves( &'w self, entity: Entity, - ) -> impl Iterator + 'w + ) -> impl Iterator + use<'w, 's, S, D, F> where - ::ReadOnly: QueryData = &'w S>, + ::ReadOnly: QueryData = &'w S>, SourceIter<'w, S>: DoubleEndedIterator, { self.iter_descendants_depth_first(entity).filter(|entity| { @@ -80,7 +80,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { entity: Entity, ) -> impl Iterator + 'w where - D::ReadOnly: QueryData = (Option<&'w R>, Option<&'w R::RelationshipTarget>)>, + D::ReadOnly: QueryData = (Option<&'w R>, Option<&'w R::RelationshipTarget>)>, { self.get(entity) .ok() @@ -103,7 +103,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { entity: Entity, ) -> DescendantIter<'w, 's, D, F, S> where - D::ReadOnly: QueryData = &'w S>, + D::ReadOnly: QueryData = &'w S>, { DescendantIter::new(self, entity) } @@ -120,7 +120,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { entity: Entity, ) -> DescendantDepthFirstIter<'w, 's, D, F, S> where - D::ReadOnly: QueryData = &'w S>, + D::ReadOnly: QueryData = &'w S>, SourceIter<'w, S>: DoubleEndedIterator, { DescendantDepthFirstIter::new(self, entity) @@ -137,7 +137,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { entity: Entity, ) -> AncestorIter<'w, 's, D, F, R> where - D::ReadOnly: QueryData = &'w R>, + D::ReadOnly: QueryData = &'w R>, { AncestorIter::new(self, entity) } @@ -148,7 +148,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// Traverses the hierarchy breadth-first. pub struct DescendantIter<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> where - D::ReadOnly: QueryData = &'w S>, + D::ReadOnly: QueryData = &'w S>, { children_query: &'w Query<'w, 's, D, F>, vecdeque: VecDeque, @@ -156,7 +156,7 @@ where impl<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> DescendantIter<'w, 's, D, F, S> where - D::ReadOnly: QueryData = &'w S>, + D::ReadOnly: QueryData = &'w S>, { /// Returns a new [`DescendantIter`]. pub fn new(children_query: &'w Query<'w, 's, D, F>, entity: Entity) -> Self { @@ -174,7 +174,7 @@ where impl<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> Iterator for DescendantIter<'w, 's, D, F, S> where - D::ReadOnly: QueryData = &'w S>, + D::ReadOnly: QueryData = &'w S>, { type Item = Entity; @@ -194,7 +194,7 @@ where /// Traverses the hierarchy depth-first. pub struct DescendantDepthFirstIter<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> where - D::ReadOnly: QueryData = &'w S>, + D::ReadOnly: QueryData = &'w S>, { children_query: &'w Query<'w, 's, D, F>, stack: SmallVec<[Entity; 8]>, @@ -203,7 +203,7 @@ where impl<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> DescendantDepthFirstIter<'w, 's, D, F, S> where - D::ReadOnly: QueryData = &'w S>, + D::ReadOnly: QueryData = &'w S>, SourceIter<'w, S>: DoubleEndedIterator, { /// Returns a new [`DescendantDepthFirstIter`]. @@ -220,7 +220,7 @@ where impl<'w, 's, D: QueryData, F: QueryFilter, S: RelationshipTarget> Iterator for DescendantDepthFirstIter<'w, 's, D, F, S> where - D::ReadOnly: QueryData = &'w S>, + D::ReadOnly: QueryData = &'w S>, SourceIter<'w, S>: DoubleEndedIterator, { type Item = Entity; @@ -239,7 +239,7 @@ where /// An [`Iterator`] of [`Entity`]s over the ancestors of an [`Entity`]. pub struct AncestorIter<'w, 's, D: QueryData, F: QueryFilter, R: Relationship> where - D::ReadOnly: QueryData = &'w R>, + D::ReadOnly: QueryData = &'w R>, { parent_query: &'w Query<'w, 's, D, F>, next: Option, @@ -247,7 +247,7 @@ where impl<'w, 's, D: QueryData, F: QueryFilter, R: Relationship> AncestorIter<'w, 's, D, F, R> where - D::ReadOnly: QueryData = &'w R>, + D::ReadOnly: QueryData = &'w R>, { /// Returns a new [`AncestorIter`]. pub fn new(parent_query: &'w Query<'w, 's, D, F>, entity: Entity) -> Self { @@ -261,7 +261,7 @@ where impl<'w, 's, D: QueryData, F: QueryFilter, R: Relationship> Iterator for AncestorIter<'w, 's, D, F, R> where - D::ReadOnly: QueryData = &'w R>, + D::ReadOnly: QueryData = &'w R>, { type Item = Entity; diff --git a/crates/bevy_ecs/src/schedule/condition.rs b/crates/bevy_ecs/src/schedule/condition.rs index 4983804dab..9a7ce5d507 100644 --- a/crates/bevy_ecs/src/schedule/condition.rs +++ b/crates/bevy_ecs/src/schedule/condition.rs @@ -1,4 +1,5 @@ -use alloc::{borrow::Cow, boxed::Box, format}; +use alloc::{boxed::Box, format}; +use bevy_utils::prelude::DebugName; use core::ops::Not; use crate::system::{ @@ -154,7 +155,7 @@ pub trait SystemCondition: let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(and); let name = format!("{} && {}", a.name(), b.name()); - CombinatorSystem::new(a, b, Cow::Owned(name)) + CombinatorSystem::new(a, b, DebugName::owned(name)) } /// Returns a new run condition that only returns `false` @@ -206,7 +207,7 @@ pub trait SystemCondition: let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(nand); let name = format!("!({} && {})", a.name(), b.name()); - CombinatorSystem::new(a, b, Cow::Owned(name)) + CombinatorSystem::new(a, b, DebugName::owned(name)) } /// Returns a new run condition that only returns `true` @@ -258,7 +259,7 @@ pub trait SystemCondition: let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(nor); let name = format!("!({} || {})", a.name(), b.name()); - CombinatorSystem::new(a, b, Cow::Owned(name)) + CombinatorSystem::new(a, b, DebugName::owned(name)) } /// Returns a new run condition that returns `true` @@ -305,7 +306,7 @@ pub trait SystemCondition: let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(or); let name = format!("{} || {}", a.name(), b.name()); - CombinatorSystem::new(a, b, Cow::Owned(name)) + CombinatorSystem::new(a, b, DebugName::owned(name)) } /// Returns a new run condition that only returns `true` @@ -357,7 +358,7 @@ pub trait SystemCondition: let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(xnor); let name = format!("!({} ^ {})", a.name(), b.name()); - CombinatorSystem::new(a, b, Cow::Owned(name)) + CombinatorSystem::new(a, b, DebugName::owned(name)) } /// Returns a new run condition that only returns `true` @@ -399,7 +400,7 @@ pub trait SystemCondition: let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(xor); let name = format!("({} ^ {})", a.name(), b.name()); - CombinatorSystem::new(a, b, Cow::Owned(name)) + CombinatorSystem::new(a, b, DebugName::owned(name)) } } diff --git a/crates/bevy_ecs/src/schedule/executor/mod.rs b/crates/bevy_ecs/src/schedule/executor/mod.rs index c1fac6a9f1..9156fc34a3 100644 --- a/crates/bevy_ecs/src/schedule/executor/mod.rs +++ b/crates/bevy_ecs/src/schedule/executor/mod.rs @@ -3,7 +3,8 @@ mod multi_threaded; mod simple; mod single_threaded; -use alloc::{borrow::Cow, vec, vec::Vec}; +use alloc::{vec, vec::Vec}; +use bevy_utils::prelude::DebugName; use core::any::TypeId; #[expect(deprecated, reason = "We still need to support this.")] @@ -158,8 +159,8 @@ impl System for ApplyDeferred { type In = (); type Out = Result<()>; - fn name(&self) -> Cow<'static, str> { - Cow::Borrowed("bevy_ecs::apply_deferred") + fn name(&self) -> DebugName { + DebugName::borrowed("bevy_ecs::apply_deferred") } fn flags(&self) -> SystemStateFlags { diff --git a/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs b/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs index fe205b19f7..b6036ee76b 100644 --- a/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs +++ b/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs @@ -342,7 +342,7 @@ impl<'scope, 'env: 'scope, 'sys> Context<'scope, 'env, 'sys> { #[cfg(feature = "std")] #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")] { - eprintln!("Encountered a panic in system `{}`!", &*system.name()); + eprintln!("Encountered a panic in system `{}`!", system.name()); } // set the payload to propagate the error { @@ -799,7 +799,7 @@ fn apply_deferred( { eprintln!( "Encountered a panic when applying buffers for system `{}`!", - &*system.name() + system.name() ); } return Err(payload); diff --git a/crates/bevy_ecs/src/schedule/executor/simple.rs b/crates/bevy_ecs/src/schedule/executor/simple.rs index 144be7f574..f0d655eab8 100644 --- a/crates/bevy_ecs/src/schedule/executor/simple.rs +++ b/crates/bevy_ecs/src/schedule/executor/simple.rs @@ -73,7 +73,7 @@ impl SystemExecutor for SimpleExecutor { #[cfg(feature = "trace")] let name = schedule.systems[system_index].system.name(); #[cfg(feature = "trace")] - let should_run_span = info_span!("check_conditions", name = &*name).entered(); + let should_run_span = info_span!("check_conditions", name = name.as_string()).entered(); let mut should_run = !self.completed_systems.contains(system_index); for set_idx in schedule.sets_with_conditions_of_systems[system_index].ones() { @@ -161,7 +161,7 @@ impl SystemExecutor for SimpleExecutor { #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")] { if let Err(payload) = std::panic::catch_unwind(f) { - eprintln!("Encountered a panic in system `{}`!", &*system.name()); + eprintln!("Encountered a panic in system `{}`!", system.name()); std::panic::resume_unwind(payload); } } diff --git a/crates/bevy_ecs/src/schedule/executor/single_threaded.rs b/crates/bevy_ecs/src/schedule/executor/single_threaded.rs index d7d46abb93..21b8d2289d 100644 --- a/crates/bevy_ecs/src/schedule/executor/single_threaded.rs +++ b/crates/bevy_ecs/src/schedule/executor/single_threaded.rs @@ -74,7 +74,7 @@ impl SystemExecutor for SingleThreadedExecutor { #[cfg(feature = "trace")] let name = schedule.systems[system_index].system.name(); #[cfg(feature = "trace")] - let should_run_span = info_span!("check_conditions", name = &*name).entered(); + let should_run_span = info_span!("check_conditions", name = name.as_string()).entered(); let mut should_run = !self.completed_systems.contains(system_index); for set_idx in schedule.sets_with_conditions_of_systems[system_index].ones() { @@ -166,7 +166,7 @@ impl SystemExecutor for SingleThreadedExecutor { #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")] { if let Err(payload) = std::panic::catch_unwind(f) { - eprintln!("Encountered a panic in system `{}`!", &*system.name()); + eprintln!("Encountered a panic in system `{}`!", system.name()); std::panic::resume_unwind(payload); } } diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 0e4f530be0..3c67f1ea07 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -2,7 +2,6 @@ clippy::module_inception, reason = "This instance of module inception is being discussed; see #17344." )] -use alloc::borrow::Cow; use alloc::{ boxed::Box, collections::{BTreeMap, BTreeSet}, @@ -12,12 +11,11 @@ use alloc::{ vec::Vec, }; use bevy_platform::collections::{HashMap, HashSet}; -use bevy_utils::{default, TypeIdMap}; +use bevy_utils::{default, prelude::DebugName, TypeIdMap}; use core::{ any::{Any, TypeId}, fmt::{Debug, Write}, }; -use disqualified::ShortName; use fixedbitset::FixedBitSet; use log::{error, info, warn}; use pass::ScheduleBuildPassObj; @@ -1694,14 +1692,14 @@ impl ScheduleGraph { #[inline] fn get_node_name_inner(&self, id: &NodeId, report_sets: bool) -> String { - let name = match id { + match id { NodeId::System(_) => { - let name = self.systems[id.index()] - .get() - .unwrap() - .system - .name() - .to_string(); + let name = self.systems[id.index()].get().unwrap().system.name(); + let name = if self.settings.use_shortnames { + name.shortname().to_string() + } else { + name.to_string() + }; if report_sets { let sets = self.names_of_sets_containing_node(id); if sets.is_empty() { @@ -1723,11 +1721,6 @@ impl ScheduleGraph { set.name() } } - }; - if self.settings.use_shortnames { - ShortName(&name).to_string() - } else { - name } } @@ -2007,7 +2000,7 @@ impl ScheduleGraph { &'a self, ambiguities: &'a [(NodeId, NodeId, Vec)], components: &'a Components, - ) -> impl Iterator>)> + 'a { + ) -> impl Iterator)> + 'a { ambiguities .iter() .map(move |(system_a, system_b, conflicts)| { diff --git a/crates/bevy_ecs/src/schedule/set.rs b/crates/bevy_ecs/src/schedule/set.rs index 2243e5019f..da91f616e9 100644 --- a/crates/bevy_ecs/src/schedule/set.rs +++ b/crates/bevy_ecs/src/schedule/set.rs @@ -1,4 +1,5 @@ use alloc::boxed::Box; +use bevy_utils::prelude::DebugName; use core::{ any::TypeId, fmt::Debug, @@ -196,7 +197,7 @@ impl SystemTypeSet { impl Debug for SystemTypeSet { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_tuple("SystemTypeSet") - .field(&format_args!("fn {}()", &core::any::type_name::())) + .field(&format_args!("fn {}()", DebugName::type_name::())) .finish() } } diff --git a/crates/bevy_ecs/src/schedule/stepping.rs b/crates/bevy_ecs/src/schedule/stepping.rs index b6de7c8215..b0d8b57fb0 100644 --- a/crates/bevy_ecs/src/schedule/stepping.rs +++ b/crates/bevy_ecs/src/schedule/stepping.rs @@ -895,9 +895,9 @@ mod tests { ($schedule:expr, $skipped_systems:expr, $($system:expr),*) => { // pull an ordered list of systems in the schedule, and save the // system TypeId, and name. - let systems: Vec<(TypeId, alloc::borrow::Cow<'static, str>)> = $schedule.systems().unwrap() + let systems: Vec<(TypeId, alloc::string::String)> = $schedule.systems().unwrap() .map(|(_, system)| { - (system.type_id(), system.name()) + (system.type_id(), system.name().as_string()) }) .collect(); diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index fc9100069c..29752ae2e5 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -3,8 +3,8 @@ use crate::{ component::{CheckChangeTicks, ComponentId, ComponentTicks, Components, Tick, TickCells}, storage::{blob_vec::BlobVec, SparseSet}, }; -use alloc::string::String; use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref}; +use bevy_utils::prelude::DebugName; use core::{cell::UnsafeCell, mem::ManuallyDrop, panic::Location}; #[cfg(feature = "std")] @@ -23,7 +23,7 @@ pub struct ResourceData { not(feature = "std"), expect(dead_code, reason = "currently only used with the std feature") )] - type_name: String, + type_name: DebugName, #[cfg(feature = "std")] origin_thread_id: Option, changed_by: MaybeLocation>>, @@ -385,7 +385,7 @@ impl Resources { data: ManuallyDrop::new(data), added_ticks: UnsafeCell::new(Tick::new(0)), changed_ticks: UnsafeCell::new(Tick::new(0)), - type_name: String::from(component_info.name()), + type_name: component_info.name(), #[cfg(feature = "std")] origin_thread_id: None, changed_by: MaybeLocation::caller().map(UnsafeCell::new), diff --git a/crates/bevy_ecs/src/system/adapter_system.rs b/crates/bevy_ecs/src/system/adapter_system.rs index ff63a3e8ce..c655ed9407 100644 --- a/crates/bevy_ecs/src/system/adapter_system.rs +++ b/crates/bevy_ecs/src/system/adapter_system.rs @@ -1,4 +1,5 @@ -use alloc::{borrow::Cow, vec::Vec}; +use alloc::vec::Vec; +use bevy_utils::prelude::DebugName; use super::{IntoSystem, ReadOnlySystem, System, SystemParamValidationError}; use crate::{ @@ -101,7 +102,7 @@ where pub struct AdapterSystem { func: Func, system: S, - name: Cow<'static, str>, + name: DebugName, } impl AdapterSystem @@ -110,7 +111,7 @@ where S: System, { /// Creates a new [`System`] that uses `func` to adapt `system`, via the [`Adapt`] trait. - pub const fn new(func: Func, system: S, name: Cow<'static, str>) -> Self { + pub const fn new(func: Func, system: S, name: DebugName) -> Self { Self { func, system, name } } } @@ -123,7 +124,7 @@ where type In = Func::In; type Out = Func::Out; - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.name.clone() } diff --git a/crates/bevy_ecs/src/system/combinator.rs b/crates/bevy_ecs/src/system/combinator.rs index 976d654a59..d48c599f2d 100644 --- a/crates/bevy_ecs/src/system/combinator.rs +++ b/crates/bevy_ecs/src/system/combinator.rs @@ -1,4 +1,5 @@ -use alloc::{borrow::Cow, format, vec::Vec}; +use alloc::{format, vec::Vec}; +use bevy_utils::prelude::DebugName; use core::marker::PhantomData; use crate::{ @@ -55,7 +56,7 @@ use super::{IntoSystem, ReadOnlySystem, System}; /// IntoSystem::into_system(resource_equals(A(1))), /// IntoSystem::into_system(resource_equals(B(1))), /// // The name of the combined system. -/// std::borrow::Cow::Borrowed("a ^ b"), +/// "a ^ b".into(), /// ))); /// # fn my_system(mut flag: ResMut) { flag.0 = true; } /// # @@ -112,14 +113,14 @@ pub struct CombinatorSystem { _marker: PhantomData Func>, a: A, b: B, - name: Cow<'static, str>, + name: DebugName, } impl CombinatorSystem { /// Creates a new system that combines two inner systems. /// /// The returned system will only be usable if `Func` implements [`Combine`]. - pub fn new(a: A, b: B, name: Cow<'static, str>) -> Self { + pub fn new(a: A, b: B, name: DebugName) -> Self { Self { _marker: PhantomData, a, @@ -138,7 +139,7 @@ where type In = Func::In; type Out = Func::Out; - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.name.clone() } @@ -271,7 +272,7 @@ where let system_a = IntoSystem::into_system(this.a); let system_b = IntoSystem::into_system(this.b); let name = format!("Pipe({}, {})", system_a.name(), system_b.name()); - PipeSystem::new(system_a, system_b, Cow::Owned(name)) + PipeSystem::new(system_a, system_b, DebugName::owned(name)) } } @@ -317,7 +318,7 @@ where pub struct PipeSystem { a: A, b: B, - name: Cow<'static, str>, + name: DebugName, } impl PipeSystem @@ -327,7 +328,7 @@ where for<'a> B::In: SystemInput = A::Out>, { /// Creates a new system that pipes two inner systems. - pub fn new(a: A, b: B, name: Cow<'static, str>) -> Self { + pub fn new(a: A, b: B, name: DebugName) -> Self { Self { a, b, name } } } @@ -341,7 +342,7 @@ where type In = A::In; type Out = B::Out; - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.name.clone() } diff --git a/crates/bevy_ecs/src/system/commands/command.rs b/crates/bevy_ecs/src/system/commands/command.rs index 84a2fdf4e9..f3fc677e47 100644 --- a/crates/bevy_ecs/src/system/commands/command.rs +++ b/crates/bevy_ecs/src/system/commands/command.rs @@ -144,10 +144,11 @@ where /// A [`Command`] that runs the given system, /// caching its [`SystemId`] in a [`CachedSystemId`](crate::system::CachedSystemId) resource. -pub fn run_system_cached(system: S) -> impl Command +pub fn run_system_cached(system: S) -> impl Command where + O: 'static, M: 'static, - S: IntoSystem<(), (), M> + Send + 'static, + S: IntoSystem<(), O, M> + Send + 'static, { move |world: &mut World| -> Result { world.run_system_cached(system)?; @@ -157,11 +158,15 @@ where /// A [`Command`] that runs the given system with the given input value, /// caching its [`SystemId`] in a [`CachedSystemId`](crate::system::CachedSystemId) resource. -pub fn run_system_cached_with(system: S, input: I::Inner<'static>) -> impl Command +pub fn run_system_cached_with( + system: S, + input: I::Inner<'static>, +) -> impl Command where I: SystemInput: Send> + Send + 'static, + O: 'static, M: 'static, - S: IntoSystem + Send + 'static, + S: IntoSystem + Send + 'static, { move |world: &mut World| -> Result { world.run_system_cached_with(system, input)?; @@ -175,7 +180,7 @@ where pub fn unregister_system(system_id: SystemId) -> impl Command where I: SystemInput + Send + 'static, - O: Send + 'static, + O: 'static, { move |world: &mut World| -> Result { world.unregister_system(system_id)?; diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 496336c247..09ac0f2478 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -917,7 +917,7 @@ impl<'w, 's> Commands<'w, 's> { /// /// It will internally return a [`RegisteredSystemError`](crate::system::system_registry::RegisteredSystemError), /// which will be handled by [logging the error at the `warn` level](warn). - pub fn run_system(&mut self, id: SystemId) { + pub fn run_system(&mut self, id: SystemId<(), O>) { self.queue(command::run_system(id).handle_error_with(warn)); } @@ -1010,7 +1010,7 @@ impl<'w, 's> Commands<'w, 's> { ) -> SystemId where I: SystemInput + Send + 'static, - O: Send + 'static, + O: 'static, { let entity = self.spawn_empty().id(); let system = RegisteredSystem::::new(Box::new(IntoSystem::into_system(system))); @@ -1035,7 +1035,7 @@ impl<'w, 's> Commands<'w, 's> { pub fn unregister_system(&mut self, system_id: SystemId) where I: SystemInput + Send + 'static, - O: Send + 'static, + O: 'static, { self.queue(command::unregister_system(system_id).handle_error_with(warn)); } @@ -1084,10 +1084,11 @@ impl<'w, 's> Commands<'w, 's> { /// consider passing them in as inputs via [`Commands::run_system_cached_with`]. /// /// If that's not an option, consider [`Commands::register_system`] instead. - pub fn run_system_cached(&mut self, system: S) + pub fn run_system_cached(&mut self, system: S) where + O: 'static, M: 'static, - S: IntoSystem<(), (), M> + Send + 'static, + S: IntoSystem<(), O, M> + Send + 'static, { self.queue(command::run_system_cached(system).handle_error_with(warn)); } @@ -1114,11 +1115,12 @@ impl<'w, 's> Commands<'w, 's> { /// consider passing them in as inputs. /// /// If that's not an option, consider [`Commands::register_system`] instead. - pub fn run_system_cached_with(&mut self, system: S, input: I::Inner<'static>) + pub fn run_system_cached_with(&mut self, system: S, input: I::Inner<'static>) where I: SystemInput: Send> + Send + 'static, + O: 'static, M: 'static, - S: IntoSystem + Send + 'static, + S: IntoSystem + Send + 'static, { self.queue(command::run_system_cached_with(system, input).handle_error_with(warn)); } diff --git a/crates/bevy_ecs/src/system/exclusive_function_system.rs b/crates/bevy_ecs/src/system/exclusive_function_system.rs index c6bdb002c2..3a053f89d5 100644 --- a/crates/bevy_ecs/src/system/exclusive_function_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_function_system.rs @@ -10,6 +10,7 @@ use crate::{ }; use alloc::{borrow::Cow, vec, vec::Vec}; +use bevy_utils::prelude::DebugName; use core::marker::PhantomData; use variadics_please::all_tuples; @@ -42,7 +43,7 @@ where /// /// Useful to give closure systems more readable and unique names for debugging and tracing. pub fn with_name(mut self, new_name: impl Into>) -> Self { - self.system_meta.set_name(new_name.into()); + self.system_meta.set_name(new_name); self } } @@ -83,7 +84,7 @@ where type Out = F::Out; #[inline] - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.system_meta.name.clone() } @@ -181,7 +182,7 @@ where check_system_change_tick( &mut self.system_meta.last_run, check, - self.system_meta.name.as_ref(), + self.system_meta.name.clone(), ); } diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index 4720860b23..6009662809 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -11,6 +11,7 @@ use crate::{ }; use alloc::{borrow::Cow, vec, vec::Vec}; +use bevy_utils::prelude::DebugName; use core::marker::PhantomData; use variadics_please::all_tuples; @@ -24,7 +25,7 @@ use super::{ /// The metadata of a [`System`]. #[derive(Clone)] pub struct SystemMeta { - pub(crate) name: Cow<'static, str>, + pub(crate) name: DebugName, // NOTE: this must be kept private. making a SystemMeta non-send is irreversible to prevent // SystemParams from overriding each other flags: SystemStateFlags, @@ -37,21 +38,21 @@ pub struct SystemMeta { impl SystemMeta { pub(crate) fn new() -> Self { - let name = core::any::type_name::(); + let name = DebugName::type_name::(); Self { - name: name.into(), + #[cfg(feature = "trace")] + system_span: info_span!("system", name = name.clone().as_string()), + #[cfg(feature = "trace")] + commands_span: info_span!("system_commands", name = name.clone().as_string()), + name, flags: SystemStateFlags::empty(), last_run: Tick::new(0), - #[cfg(feature = "trace")] - system_span: info_span!("system", name = name), - #[cfg(feature = "trace")] - commands_span: info_span!("system_commands", name = name), } } /// Returns the system's name #[inline] - pub fn name(&self) -> &str { + pub fn name(&self) -> &DebugName { &self.name } @@ -67,7 +68,7 @@ impl SystemMeta { self.system_span = info_span!("system", name = name); self.commands_span = info_span!("system_commands", name = name); } - self.name = new_name; + self.name = new_name.into(); } /// Returns true if the system is [`Send`]. @@ -600,7 +601,7 @@ where type Out = F::Out; #[inline] - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.system_meta.name.clone() } @@ -712,7 +713,7 @@ where check_system_change_tick( &mut self.system_meta.last_run, check, - self.system_meta.name.as_ref(), + self.system_meta.name.clone(), ); } diff --git a/crates/bevy_ecs/src/system/observer_system.rs b/crates/bevy_ecs/src/system/observer_system.rs index e99a86b64c..8e927d9529 100644 --- a/crates/bevy_ecs/src/system/observer_system.rs +++ b/crates/bevy_ecs/src/system/observer_system.rs @@ -1,4 +1,5 @@ -use alloc::{borrow::Cow, vec::Vec}; +use alloc::vec::Vec; +use bevy_utils::prelude::DebugName; use core::marker::PhantomData; use crate::{ @@ -112,7 +113,7 @@ where type Out = Result; #[inline] - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.observer.name() } diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 292b68d242..571c963a95 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -1,3 +1,5 @@ +use bevy_utils::prelude::DebugName; + use crate::{ batching::BatchingStrategy, component::Tick, @@ -1185,7 +1187,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// [`par_iter_mut`]: Self::par_iter_mut /// [`World`]: crate::world::World #[inline] - pub fn par_iter(&self) -> QueryParIter<'_, '_, D::ReadOnly, F> { + pub fn par_iter(&self) -> QueryParIter<'_, 's, D::ReadOnly, F> { self.as_readonly().par_iter_inner() } @@ -1220,7 +1222,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// [`par_iter`]: Self::par_iter /// [`World`]: crate::world::World #[inline] - pub fn par_iter_mut(&mut self) -> QueryParIter<'_, '_, D, F> { + pub fn par_iter_mut(&mut self) -> QueryParIter<'_, 's, D, F> { self.reborrow().par_iter_inner() } @@ -1280,7 +1282,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn par_iter_many>( &self, entities: EntityList, - ) -> QueryParManyIter<'_, '_, D::ReadOnly, F, EntityList::Item> { + ) -> QueryParManyIter<'_, 's, D::ReadOnly, F, EntityList::Item> { QueryParManyIter { world: self.world, state: self.state.as_readonly(), @@ -1309,7 +1311,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn par_iter_many_unique>( &self, entities: EntityList, - ) -> QueryParManyUniqueIter<'_, '_, D::ReadOnly, F, EntityList::Item> { + ) -> QueryParManyUniqueIter<'_, 's, D::ReadOnly, F, EntityList::Item> { QueryParManyUniqueIter { world: self.world, state: self.state.as_readonly(), @@ -1338,7 +1340,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn par_iter_many_unique_mut>( &mut self, entities: EntityList, - ) -> QueryParManyUniqueIter<'_, '_, D, F, EntityList::Item> { + ) -> QueryParManyUniqueIter<'_, 's, D, F, EntityList::Item> { QueryParManyUniqueIter { world: self.world, state: self.state, @@ -1383,7 +1385,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// /// - [`get_mut`](Self::get_mut) to get a mutable query item. #[inline] - pub fn get(&self, entity: Entity) -> Result, QueryEntityError> { + pub fn get(&self, entity: Entity) -> Result, QueryEntityError> { self.as_readonly().get_inner(entity) } @@ -1434,7 +1436,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn get_many( &self, entities: [Entity; N], - ) -> Result<[ROQueryItem<'_, D>; N], QueryEntityError> { + ) -> Result<[ROQueryItem<'_, 's, D>; N], QueryEntityError> { // Note that we call a separate `*_inner` method from `get_many_mut` // because we don't need to check for duplicates. self.as_readonly().get_many_inner(entities) @@ -1485,7 +1487,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn get_many_unique( &self, entities: UniqueEntityArray, - ) -> Result<[ROQueryItem<'_, D>; N], QueryEntityError> { + ) -> Result<[ROQueryItem<'_, 's, D>; N], QueryEntityError> { self.as_readonly().get_many_unique_inner(entities) } @@ -1519,7 +1521,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// /// - [`get`](Self::get) to get a read-only query item. #[inline] - pub fn get_mut(&mut self, entity: Entity) -> Result, QueryEntityError> { + pub fn get_mut(&mut self, entity: Entity) -> Result, QueryEntityError> { self.reborrow().get_inner(entity) } @@ -1534,7 +1536,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// /// - [`get_mut`](Self::get_mut) to get the item using a mutable borrow of the [`Query`]. #[inline] - pub fn get_inner(self, entity: Entity) -> Result, QueryEntityError> { + pub fn get_inner(self, entity: Entity) -> Result, QueryEntityError> { // SAFETY: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { @@ -1576,8 +1578,18 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { D::set_archetype(&mut fetch, &self.state.fetch_state, archetype, table); F::set_archetype(&mut filter, &self.state.filter_state, archetype, table); - if F::filter_fetch(&mut filter, entity, location.table_row) { - Ok(D::fetch(&mut fetch, entity, location.table_row)) + if F::filter_fetch( + &self.state.filter_state, + &mut filter, + entity, + location.table_row, + ) { + Ok(D::fetch( + &self.state.fetch_state, + &mut fetch, + entity, + location.table_row, + )) } else { Err(QueryEntityError::QueryDoesNotMatch( entity, @@ -1658,7 +1670,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn get_many_mut( &mut self, entities: [Entity; N], - ) -> Result<[D::Item<'_>; N], QueryEntityError> { + ) -> Result<[D::Item<'_, 's>; N], QueryEntityError> { self.reborrow().get_many_mut_inner(entities) } @@ -1726,7 +1738,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn get_many_unique_mut( &mut self, entities: UniqueEntityArray, - ) -> Result<[D::Item<'_>; N], QueryEntityError> { + ) -> Result<[D::Item<'_, 's>; N], QueryEntityError> { self.reborrow().get_many_unique_inner(entities) } @@ -1745,7 +1757,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn get_many_mut_inner( self, entities: [Entity; N], - ) -> Result<[D::Item<'w>; N], QueryEntityError> { + ) -> Result<[D::Item<'w, 's>; N], QueryEntityError> { // Verify that all entities are unique for i in 0..N { for j in 0..i { @@ -1773,7 +1785,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn get_many_inner( self, entities: [Entity; N], - ) -> Result<[D::Item<'w>; N], QueryEntityError> + ) -> Result<[D::Item<'w, 's>; N], QueryEntityError> where D: ReadOnlyQueryData, { @@ -1795,7 +1807,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn get_many_unique_inner( self, entities: UniqueEntityArray, - ) -> Result<[D::Item<'w>; N], QueryEntityError> { + ) -> Result<[D::Item<'w, 's>; N], QueryEntityError> { // SAFETY: All entities are unique, so the results don't alias. unsafe { self.get_many_impl(entities.into_inner()) } } @@ -1810,7 +1822,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { unsafe fn get_many_impl( self, entities: [Entity; N], - ) -> Result<[D::Item<'w>; N], QueryEntityError> { + ) -> Result<[D::Item<'w, 's>; N], QueryEntityError> { let mut values = [(); N].map(|_| MaybeUninit::uninit()); for (value, entity) in core::iter::zip(&mut values, entities) { @@ -1838,7 +1850,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// /// - [`get_mut`](Self::get_mut) for the safe version. #[inline] - pub unsafe fn get_unchecked(&self, entity: Entity) -> Result, QueryEntityError> { + pub unsafe fn get_unchecked( + &self, + entity: Entity, + ) -> Result, QueryEntityError> { // SAFETY: The caller promises that this will not result in multiple mutable references. unsafe { self.reborrow_unsafe() }.get_inner(entity) } @@ -1874,7 +1889,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// /// - [`single_mut`](Self::single_mut) to get the mutable query item. #[inline] - pub fn single(&self) -> Result, QuerySingleError> { + pub fn single(&self) -> Result, QuerySingleError> { self.as_readonly().single_inner() } @@ -1903,7 +1918,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// /// - [`single`](Self::single) to get the read-only query item. #[inline] - pub fn single_mut(&mut self) -> Result, QuerySingleError> { + pub fn single_mut(&mut self) -> Result, QuerySingleError> { self.reborrow().single_inner() } @@ -1935,15 +1950,15 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { /// - [`single_mut`](Self::single_mut) to get the mutable query item. /// - [`single_inner`](Self::single_inner) for the panicking version. #[inline] - pub fn single_inner(self) -> Result, QuerySingleError> { + pub fn single_inner(self) -> Result, QuerySingleError> { let mut query = self.into_iter(); let first = query.next(); let extra = query.next().is_some(); match (first, extra) { (Some(r), false) => Ok(r), - (None, _) => Err(QuerySingleError::NoEntities(core::any::type_name::())), - (Some(_), _) => Err(QuerySingleError::MultipleEntities(core::any::type_name::< + (None, _) => Err(QuerySingleError::NoEntities(DebugName::type_name::())), + (Some(_), _) => Err(QuerySingleError::MultipleEntities(DebugName::type_name::< Self, >())), } @@ -2447,7 +2462,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { } impl<'w, 's, D: QueryData, F: QueryFilter> IntoIterator for Query<'w, 's, D, F> { - type Item = D::Item<'w>; + type Item = D::Item<'w, 's>; type IntoIter = QueryIter<'w, 's, D, F>; fn into_iter(self) -> Self::IntoIter { @@ -2460,7 +2475,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> IntoIterator for Query<'w, 's, D, F> } impl<'w, 's, D: QueryData, F: QueryFilter> IntoIterator for &'w Query<'_, 's, D, F> { - type Item = ROQueryItem<'w, D>; + type Item = ROQueryItem<'w, 's, D>; type IntoIter = QueryIter<'w, 's, D::ReadOnly, F>; fn into_iter(self) -> Self::IntoIter { @@ -2469,7 +2484,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> IntoIterator for &'w Query<'_, 's, D, } impl<'w, 's, D: QueryData, F: QueryFilter> IntoIterator for &'w mut Query<'_, 's, D, F> { - type Item = D::Item<'w>; + type Item = D::Item<'w, 's>; type IntoIter = QueryIter<'w, 's, D, F>; fn into_iter(self) -> Self::IntoIter { @@ -2584,28 +2599,28 @@ impl<'w, 'q, Q: QueryData, F: QueryFilter> From<&'q mut Query<'w, '_, Q, F>> /// ``` /// Note that because [`Single`] implements [`Deref`] and [`DerefMut`], methods and fields like `health` can be accessed directly. /// You can also access the underlying data manually, by calling `.deref`/`.deref_mut`, or by using the `*` operator. -pub struct Single<'w, D: QueryData, F: QueryFilter = ()> { - pub(crate) item: D::Item<'w>, +pub struct Single<'w, 's, D: QueryData, F: QueryFilter = ()> { + pub(crate) item: D::Item<'w, 's>, pub(crate) _filter: PhantomData, } -impl<'w, D: QueryData, F: QueryFilter> Deref for Single<'w, D, F> { - type Target = D::Item<'w>; +impl<'w, 's, D: QueryData, F: QueryFilter> Deref for Single<'w, 's, D, F> { + type Target = D::Item<'w, 's>; fn deref(&self) -> &Self::Target { &self.item } } -impl<'w, D: QueryData, F: QueryFilter> DerefMut for Single<'w, D, F> { +impl<'w, 's, D: QueryData, F: QueryFilter> DerefMut for Single<'w, 's, D, F> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.item } } -impl<'w, D: QueryData, F: QueryFilter> Single<'w, D, F> { +impl<'w, 's, D: QueryData, F: QueryFilter> Single<'w, 's, D, F> { /// Returns the inner item with ownership. - pub fn into_inner(self) -> D::Item<'w> { + pub fn into_inner(self) -> D::Item<'w, 's> { self.item } } diff --git a/crates/bevy_ecs/src/system/schedule_system.rs b/crates/bevy_ecs/src/system/schedule_system.rs index ab2d9d31fb..ca4bdd4460 100644 --- a/crates/bevy_ecs/src/system/schedule_system.rs +++ b/crates/bevy_ecs/src/system/schedule_system.rs @@ -1,4 +1,5 @@ -use alloc::{borrow::Cow, vec::Vec}; +use alloc::vec::Vec; +use bevy_utils::prelude::DebugName; use crate::{ component::{CheckChangeTicks, ComponentId, Tick}, @@ -25,7 +26,7 @@ impl> System for InfallibleSystemWrapper { type Out = Result; #[inline] - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.0.name() } @@ -137,10 +138,9 @@ where T: Send + Sync + 'static, { type In = (); - type Out = S::Out; - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.system.name() } @@ -231,10 +231,9 @@ where T: FromWorld + Send + Sync + 'static, { type In = (); - type Out = S::Out; - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.system.name() } diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index 639b9f8b71..863baf2ea6 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -2,6 +2,7 @@ clippy::module_inception, reason = "This instance of module inception is being discussed; see #17353." )] +use bevy_utils::prelude::DebugName; use bitflags::bitflags; use core::fmt::Debug; use log::warn; @@ -15,7 +16,7 @@ use crate::{ world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World}, }; -use alloc::{borrow::Cow, boxed::Box, vec::Vec}; +use alloc::{boxed::Box, vec::Vec}; use core::any::TypeId; use super::{IntoSystem, SystemParamValidationError}; @@ -49,8 +50,9 @@ pub trait System: Send + Sync + 'static { type In: SystemInput; /// The system's output. type Out; + /// Returns the system's name. - fn name(&self) -> Cow<'static, str>; + fn name(&self) -> DebugName; /// Returns the [`TypeId`] of the underlying system type. #[inline] fn type_id(&self) -> TypeId { @@ -227,7 +229,7 @@ pub type BoxedSystem = Box>; pub(crate) fn check_system_change_tick( last_run: &mut Tick, check: CheckChangeTicks, - system_name: &str, + system_name: DebugName, ) { if last_run.check_tick(check) { let age = check.present_tick().relative_to(*last_run).get(); @@ -398,7 +400,7 @@ pub enum RunSystemError { #[error("System {system} did not run due to failed parameter validation: {err}")] InvalidParams { /// The identifier of the system that was run. - system: Cow<'static, str>, + system: DebugName, /// The returned parameter validation error. err: SystemParamValidationError, }, diff --git a/crates/bevy_ecs/src/system/system_name.rs b/crates/bevy_ecs/src/system/system_name.rs index 1205ff055c..f38a5eb1aa 100644 --- a/crates/bevy_ecs/src/system/system_name.rs +++ b/crates/bevy_ecs/src/system/system_name.rs @@ -5,9 +5,8 @@ use crate::{ system::{ExclusiveSystemParam, ReadOnlySystemParam, SystemMeta, SystemParam}, world::unsafe_world_cell::UnsafeWorldCell, }; -use alloc::borrow::Cow; -use core::ops::Deref; -use derive_more::derive::{AsRef, Display, Into}; +use bevy_utils::prelude::DebugName; +use derive_more::derive::{Display, Into}; /// [`SystemParam`] that returns the name of the system which it is used in. /// @@ -35,21 +34,13 @@ use derive_more::derive::{AsRef, Display, Into}; /// logger.log("Hello"); /// } /// ``` -#[derive(Debug, Into, Display, AsRef)] -#[as_ref(str)] -pub struct SystemName(Cow<'static, str>); +#[derive(Debug, Into, Display)] +pub struct SystemName(DebugName); impl SystemName { /// Gets the name of the system. - pub fn name(&self) -> &str { - &self.0 - } -} - -impl Deref for SystemName { - type Target = str; - fn deref(&self) -> &Self::Target { - self.name() + pub fn name(&self) -> DebugName { + self.0.clone() } } @@ -104,7 +95,7 @@ mod tests { #[test] fn test_system_name_regular_param() { fn testing(name: SystemName) -> String { - name.name().to_owned() + name.name().as_string() } let mut world = World::default(); @@ -116,7 +107,7 @@ mod tests { #[test] fn test_system_name_exclusive_param() { fn testing(_world: &mut World, name: SystemName) -> String { - name.name().to_owned() + name.name().as_string() } let mut world = World::default(); @@ -130,7 +121,7 @@ mod tests { let mut world = World::default(); let system = IntoSystem::into_system(|name: SystemName| name.name().to_owned()).with_name("testing"); - let name = world.run_system_once(system).unwrap(); + let name = world.run_system_once(system).unwrap().as_string(); assert_eq!(name, "testing"); } @@ -140,7 +131,7 @@ mod tests { let system = IntoSystem::into_system(|_world: &mut World, name: SystemName| name.name().to_owned()) .with_name("testing"); - let name = world.run_system_once(system).unwrap(); + let name = world.run_system_once(system).unwrap().as_string(); assert_eq!(name, "testing"); } } diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 8bc1787dc4..609ad58ec9 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -25,6 +25,7 @@ use alloc::{ pub use bevy_ecs_macros::SystemParam; use bevy_platform::cell::SyncCell; use bevy_ptr::UnsafeCellDeref; +use bevy_utils::prelude::DebugName; use core::{ any::Any, fmt::{Debug, Display}, @@ -32,7 +33,6 @@ use core::{ ops::{Deref, DerefMut}, panic::Location, }; -use disqualified::ShortName; use thiserror::Error; use super::Populated; @@ -343,8 +343,8 @@ unsafe impl SystemParam for Qu ) { assert_component_access_compatibility( &system_meta.name, - core::any::type_name::(), - core::any::type_name::(), + DebugName::type_name::(), + DebugName::type_name::(), component_access_set, &state.component_access, world, @@ -368,9 +368,9 @@ unsafe impl SystemParam for Qu } fn assert_component_access_compatibility( - system_name: &str, - query_type: &'static str, - filter_type: &'static str, + system_name: &DebugName, + query_type: DebugName, + filter_type: DebugName, system_access: &FilteredAccessSet, current: &FilteredAccess, world: &World, @@ -384,14 +384,16 @@ fn assert_component_access_compatibility( if !accesses.is_empty() { accesses.push(' '); } - panic!("error[B0001]: Query<{}, {}> in system {system_name} accesses component(s) {accesses}in a way that conflicts with a previous system parameter. Consider using `Without` to create disjoint Queries or merging conflicting Queries into a `ParamSet`. See: https://bevy.org/learn/errors/b0001", ShortName(query_type), ShortName(filter_type)); + panic!("error[B0001]: Query<{}, {}> in system {system_name} accesses component(s) {accesses}in a way that conflicts with a previous system parameter. Consider using `Without` to create disjoint Queries or merging conflicting Queries into a `ParamSet`. See: https://bevy.org/learn/errors/b0001", query_type.shortname(), filter_type.shortname()); } // SAFETY: Relevant query ComponentId access is applied to SystemMeta. If // this Query conflicts with any prior access, a panic will occur. -unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam for Single<'a, D, F> { +unsafe impl<'a, 'b, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam + for Single<'a, 'b, D, F> +{ type State = QueryState; - type Item<'w, 's> = Single<'w, D, F>; + type Item<'w, 's> = Single<'w, 's, D, F>; fn init_state(world: &mut World) -> Self::State { Query::init_state(world) @@ -451,8 +453,8 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam fo } // SAFETY: QueryState is constrained to read-only fetches, so it only reads World. -unsafe impl<'a, D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> ReadOnlySystemParam - for Single<'a, D, F> +unsafe impl<'a, 'b, D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> ReadOnlySystemParam + for Single<'a, 'b, D, F> { } @@ -765,9 +767,10 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { assert!( !combined_access.has_resource_write(component_id), "error[B0002]: Res<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - core::any::type_name::(), + DebugName::type_name::(), system_meta.name, ); + component_access_set.add_unfiltered_resource_read(component_id); } @@ -805,8 +808,8 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { panic!( "Resource requested by {} does not exist: {}", system_meta.name, - core::any::type_name::() - ) + DebugName::type_name::() + ); }); Res { value: ptr.deref(), @@ -841,11 +844,11 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { if combined_access.has_resource_write(component_id) { panic!( "error[B0002]: ResMut<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - core::any::type_name::(), system_meta.name); + DebugName::type_name::(), system_meta.name); } else if combined_access.has_resource_read(component_id) { panic!( "error[B0002]: ResMut<{}> in system {} conflicts with a previous Res<{0}> access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - core::any::type_name::(), system_meta.name); + DebugName::type_name::(), system_meta.name); } component_access_set.add_unfiltered_resource_write(component_id); } @@ -883,8 +886,8 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { panic!( "Resource requested by {} does not exist: {}", system_meta.name, - core::any::type_name::() - ) + DebugName::type_name::() + ); }); ResMut { value: value.value.deref_mut::(), @@ -1433,7 +1436,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { assert!( !combined_access.has_resource_write(component_id), "error[B0002]: NonSend<{}> in system {} conflicts with a previous mutable resource access ({0}). Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - core::any::type_name::(), + DebugName::type_name::(), system_meta.name, ); component_access_set.add_unfiltered_resource_read(component_id); @@ -1473,7 +1476,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { panic!( "Non-send resource requested by {} does not exist: {}", system_meta.name, - core::any::type_name::() + DebugName::type_name::() ) }); @@ -1509,11 +1512,11 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { if combined_access.has_component_write(component_id) { panic!( "error[B0002]: NonSendMut<{}> in system {} conflicts with a previous mutable resource access ({0}). Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - core::any::type_name::(), system_meta.name); + DebugName::type_name::(), system_meta.name); } else if combined_access.has_component_read(component_id) { panic!( "error[B0002]: NonSendMut<{}> in system {} conflicts with a previous immutable resource access ({0}). Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - core::any::type_name::(), system_meta.name); + DebugName::type_name::(), system_meta.name); } component_access_set.add_unfiltered_resource_write(component_id); } @@ -1552,8 +1555,8 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { panic!( "Non-send resource requested by {} does not exist: {}", system_meta.name, - core::any::type_name::() - ) + DebugName::type_name::() + ); }); NonSendMut { value: ptr.assert_unique().deref_mut(), @@ -2805,7 +2808,7 @@ pub struct SystemParamValidationError { /// A string identifying the invalid parameter. /// This is usually the type name of the parameter. - pub param: Cow<'static, str>, + pub param: DebugName, /// A string identifying the field within a parameter using `#[derive(SystemParam)]`. /// This will be an empty string for other parameters. @@ -2837,7 +2840,7 @@ impl SystemParamValidationError { Self { skipped, message: message.into(), - param: Cow::Borrowed(core::any::type_name::()), + param: DebugName::type_name::(), field: field.into(), } } @@ -2848,7 +2851,7 @@ impl Display for SystemParamValidationError { write!( fmt, "Parameter `{}{}` failed validation: {}", - ShortName(&self.param), + self.param.shortname(), self.field, self.message )?; diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 7fdc22076e..f84f38b4d9 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -651,6 +651,19 @@ mod tests { assert_eq!(output, NonCopy(3)); } + #[test] + fn fallible_system() { + fn sys() -> Result<()> { + Err("error")?; + Ok(()) + } + + let mut world = World::new(); + let fallible_system_id = world.register_system(sys); + let output = world.run_system(fallible_system_id); + assert!(matches!(output, Ok(Err(_)))); + } + #[test] fn exclusive_system() { let mut world = World::new(); @@ -751,19 +764,54 @@ mod tests { assert!(matches!(output, Ok(x) if x == four())); } + #[test] + fn cached_fallible_system() { + fn sys() -> Result<()> { + Err("error")?; + Ok(()) + } + + let mut world = World::new(); + let fallible_system_id = world.register_system_cached(sys); + let output = world.run_system(fallible_system_id); + assert!(matches!(output, Ok(Err(_)))); + let output = world.run_system_cached(sys); + assert!(matches!(output, Ok(Err(_)))); + let output = world.run_system_cached_with(sys, ()); + assert!(matches!(output, Ok(Err(_)))); + } + #[test] fn cached_system_commands() { fn sys(mut counter: ResMut) { - counter.0 = 1; + counter.0 += 1; } let mut world = World::new(); world.insert_resource(Counter(0)); - world.commands().run_system_cached(sys); world.flush_commands(); - assert_eq!(world.resource::().0, 1); + world.commands().run_system_cached_with(sys, ()); + world.flush_commands(); + assert_eq!(world.resource::().0, 2); + } + + #[test] + fn cached_fallible_system_commands() { + fn sys(mut counter: ResMut) -> Result { + counter.0 += 1; + Ok(()) + } + + let mut world = World::new(); + world.insert_resource(Counter(0)); + world.commands().run_system_cached(sys); + world.flush_commands(); + assert_eq!(world.resource::().0, 1); + world.commands().run_system_cached_with(sys, ()); + world.flush_commands(); + assert_eq!(world.resource::().0, 2); } #[test] diff --git a/crates/bevy_ecs/src/traversal.rs b/crates/bevy_ecs/src/traversal.rs index d6527a10fa..577720fd5d 100644 --- a/crates/bevy_ecs/src/traversal.rs +++ b/crates/bevy_ecs/src/traversal.rs @@ -1,6 +1,10 @@ //! A trait for components that let you traverse the ECS. -use crate::{entity::Entity, query::ReadOnlyQueryData, relationship::Relationship}; +use crate::{ + entity::Entity, + query::{ReadOnlyQueryData, ReleaseStateQueryData}, + relationship::Relationship, +}; /// A component that can point to another entity, and which can be used to define a path through the ECS. /// @@ -20,13 +24,13 @@ use crate::{entity::Entity, query::ReadOnlyQueryData, relationship::Relationship /// [specify the direction]: crate::event::EntityEvent::Traversal /// [event propagation]: crate::observer::On::propagate /// [observers]: crate::observer::Observer -pub trait Traversal: ReadOnlyQueryData { +pub trait Traversal: ReadOnlyQueryData + ReleaseStateQueryData { /// Returns the next entity to visit. - fn traverse(item: Self::Item<'_>, data: &D) -> Option; + fn traverse(item: Self::Item<'_, '_>, data: &D) -> Option; } impl Traversal for () { - fn traverse(_: Self::Item<'_>, _data: &D) -> Option { + fn traverse(_: Self::Item<'_, '_>, _data: &D) -> Option { None } } @@ -39,7 +43,7 @@ impl Traversal for () { /// /// [event propagation]: crate::observer::On::propagate impl Traversal for &R { - fn traverse(item: Self::Item<'_>, _data: &D) -> Option { + fn traverse(item: Self::Item<'_, '_>, _data: &D) -> Option { Some(item.get()) } } diff --git a/crates/bevy_ecs/src/world/deferred_world.rs b/crates/bevy_ecs/src/world/deferred_world.rs index 7cfbf72240..1c56ebb2d4 100644 --- a/crates/bevy_ecs/src/world/deferred_world.rs +++ b/crates/bevy_ecs/src/world/deferred_world.rs @@ -1,5 +1,7 @@ use core::ops::Deref; +use bevy_utils::prelude::DebugName; + use crate::{ archetype::Archetype, change_detection::{MaybeLocation, MutUntyped}, @@ -460,7 +462,7 @@ impl<'w> DeferredWorld<'w> { Did you forget to add it using `app.insert_resource` / `app.init_resource`? Resources are also implicitly added via `app.add_event`, and can be added by plugins.", - core::any::type_name::() + DebugName::type_name::() ), } } @@ -489,7 +491,7 @@ impl<'w> DeferredWorld<'w> { "Requested non-send resource {} does not exist in the `World`. Did you forget to add it using `app.insert_non_send_resource` / `app.init_non_send_resource`? Non-send resources can also be added by plugins.", - core::any::type_name::() + DebugName::type_name::() ), } } @@ -532,7 +534,7 @@ impl<'w> DeferredWorld<'w> { let Some(mut events_resource) = self.get_resource_mut::>() else { log::error!( "Unable to send event `{}`\n\tEvent must be added to the app with `add_event()`\n\thttps://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event ", - core::any::type_name::() + DebugName::type_name::() ); return None; }; diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index c3f6b93123..917e737487 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -16,7 +16,7 @@ use crate::{ event::EntityEvent, lifecycle::{DESPAWN, REMOVE, REPLACE}, observer::Observer, - query::{Access, DebugCheckedUnwrap, ReadOnlyQueryData}, + query::{Access, DebugCheckedUnwrap, ReadOnlyQueryData, ReleaseStateQueryData}, relationship::RelationshipHookMode, resource::Resource, system::IntoObserverSystem, @@ -285,14 +285,16 @@ impl<'w> EntityRef<'w> { /// # Panics /// /// If the entity does not have the components required by the query `Q`. - pub fn components(&self) -> Q::Item<'w> { + pub fn components(&self) -> Q::Item<'w, 'static> { self.get_components::() .expect("Query does not match the current entity") } /// Returns read-only components for the current entity that match the query `Q`, /// or `None` if the entity does not have the components required by the query `Q`. - pub fn get_components(&self) -> Option> { + pub fn get_components( + &self, + ) -> Option> { // SAFETY: We have read-only access to all components of this entity. unsafe { self.cell.get_components::() } } @@ -558,13 +560,15 @@ impl<'w> EntityMut<'w> { /// # Panics /// /// If the entity does not have the components required by the query `Q`. - pub fn components(&self) -> Q::Item<'_> { + pub fn components(&self) -> Q::Item<'_, 'static> { self.as_readonly().components::() } /// Returns read-only components for the current entity that match the query `Q`, /// or `None` if the entity does not have the components required by the query `Q`. - pub fn get_components(&self) -> Option> { + pub fn get_components( + &self, + ) -> Option> { self.as_readonly().get_components::() } @@ -1325,7 +1329,7 @@ impl<'w> EntityWorldMut<'w> { /// If the entity does not have the components required by the query `Q` or if the entity /// has been despawned while this `EntityWorldMut` is still alive. #[inline] - pub fn components(&self) -> Q::Item<'_> { + pub fn components(&self) -> Q::Item<'_, 'static> { self.as_readonly().components::() } @@ -1336,7 +1340,9 @@ impl<'w> EntityWorldMut<'w> { /// /// If the entity has been despawned while this `EntityWorldMut` is still alive. #[inline] - pub fn get_components(&self) -> Option> { + pub fn get_components( + &self, + ) -> Option> { self.as_readonly().get_components::() } diff --git a/crates/bevy_ecs/src/world/error.rs b/crates/bevy_ecs/src/world/error.rs index 7a55233bf9..1bfaae1bbd 100644 --- a/crates/bevy_ecs/src/world/error.rs +++ b/crates/bevy_ecs/src/world/error.rs @@ -1,6 +1,7 @@ //! Contains error types returned by bevy's schedule. use alloc::vec::Vec; +use bevy_utils::prelude::DebugName; use crate::{ component::ComponentId, @@ -24,7 +25,7 @@ pub struct TryRunScheduleError(pub InternedScheduleLabel); #[error("Could not insert bundles of type {bundle_type} into the entities with the following IDs because they do not exist: {entities:?}")] pub struct TryInsertBatchError { /// The bundles' type name. - pub bundle_type: &'static str, + pub bundle_type: DebugName, /// The IDs of the provided entities that do not exist. pub entities: Vec, } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 42ee15de3d..caf0dfc55d 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -25,6 +25,7 @@ use crate::{ prelude::{Add, Despawn, Insert, Remove, Replace}, }; pub use bevy_ecs_macros::FromWorld; +use bevy_utils::prelude::DebugName; pub use deferred_world::DeferredWorld; pub use entity_fetch::{EntityFetcher, WorldEntityFetch}; pub use entity_ref::{ @@ -264,6 +265,12 @@ impl World { &self.removed_components } + /// Retrieves this world's [`Observers`] list + #[inline] + pub fn observers(&self) -> &Observers { + &self.observers + } + /// Creates a new [`Commands`] instance that writes to the world's command queue /// Use [`World::flush`] to apply all queued commands #[inline] @@ -2087,7 +2094,7 @@ impl World { Did you forget to add it using `app.insert_resource` / `app.init_resource`? Resources are also implicitly added via `app.add_event`, and can be added by plugins.", - core::any::type_name::() + DebugName::type_name::() ), } } @@ -2111,7 +2118,7 @@ impl World { Did you forget to add it using `app.insert_resource` / `app.init_resource`? Resources are also implicitly added via `app.add_event`, and can be added by plugins.", - core::any::type_name::() + DebugName::type_name::() ), } } @@ -2135,7 +2142,7 @@ impl World { Did you forget to add it using `app.insert_resource` / `app.init_resource`? Resources are also implicitly added via `app.add_event`, and can be added by plugins.", - core::any::type_name::() + DebugName::type_name::() ), } } @@ -2299,7 +2306,7 @@ impl World { "Requested non-send resource {} does not exist in the `World`. Did you forget to add it using `app.insert_non_send_resource` / `app.init_non_send_resource`? Non-send resources can also be added by plugins.", - core::any::type_name::() + DebugName::type_name::() ), } } @@ -2321,7 +2328,7 @@ impl World { "Requested non-send resource {} does not exist in the `World`. Did you forget to add it using `app.insert_non_send_resource` / `app.init_non_send_resource`? Non-send resources can also be added by plugins.", - core::any::type_name::() + DebugName::type_name::() ), } } @@ -2660,7 +2667,7 @@ impl World { Ok(()) } else { Err(TryInsertBatchError { - bundle_type: core::any::type_name::(), + bundle_type: DebugName::type_name::(), entities: invalid_entities, }) } @@ -2694,7 +2701,7 @@ impl World { #[track_caller] pub fn resource_scope(&mut self, f: impl FnOnce(&mut World, Mut) -> U) -> U { self.try_resource_scope(f) - .unwrap_or_else(|| panic!("resource does not exist: {}", core::any::type_name::())) + .unwrap_or_else(|| panic!("resource does not exist: {}", DebugName::type_name::())) } /// Temporarily removes the requested resource from this [`World`] if it exists, runs custom user code, @@ -2734,7 +2741,7 @@ impl World { assert!(!self.contains_resource::(), "Resource `{}` was inserted during a call to World::resource_scope.\n\ This is not allowed as the original resource is reinserted to the world after the closure is invoked.", - core::any::type_name::()); + DebugName::type_name::()); OwningPtr::make(value, |ptr| { // SAFETY: pointer is of type R @@ -2775,7 +2782,7 @@ impl World { let Some(mut events_resource) = self.get_resource_mut::>() else { log::error!( "Unable to send event `{}`\n\tEvent must be added to the app with `add_event()`\n\thttps://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event ", - core::any::type_name::() + DebugName::type_name::() ); return None; }; @@ -3752,6 +3759,7 @@ mod tests { }; use bevy_ecs_macros::Component; use bevy_platform::collections::{HashMap, HashSet}; + use bevy_utils::prelude::DebugName; use core::{ any::TypeId, panic, @@ -3935,12 +3943,12 @@ mod tests { let mut iter = world.iter_resources(); let (info, ptr) = iter.next().unwrap(); - assert_eq!(info.name(), core::any::type_name::()); + assert_eq!(info.name(), DebugName::type_name::()); // SAFETY: We know that the resource is of type `TestResource` assert_eq!(unsafe { ptr.deref::().0 }, 42); let (info, ptr) = iter.next().unwrap(); - assert_eq!(info.name(), core::any::type_name::()); + assert_eq!(info.name(), DebugName::type_name::()); assert_eq!( // SAFETY: We know that the resource is of type `TestResource2` unsafe { &ptr.deref::().0 }, @@ -3963,14 +3971,14 @@ mod tests { let mut iter = world.iter_resources_mut(); let (info, mut mut_untyped) = iter.next().unwrap(); - assert_eq!(info.name(), core::any::type_name::()); + assert_eq!(info.name(), DebugName::type_name::()); // SAFETY: We know that the resource is of type `TestResource` unsafe { mut_untyped.as_mut().deref_mut::().0 = 43; }; let (info, mut mut_untyped) = iter.next().unwrap(); - assert_eq!(info.name(), core::any::type_name::()); + assert_eq!(info.name(), DebugName::type_name::()); // SAFETY: We know that the resource is of type `TestResource2` unsafe { mut_untyped.as_mut().deref_mut::().0 = "Hello, world?".to_string(); diff --git a/crates/bevy_ecs/src/world/reflect.rs b/crates/bevy_ecs/src/world/reflect.rs index 5ecdf88156..aada63bf61 100644 --- a/crates/bevy_ecs/src/world/reflect.rs +++ b/crates/bevy_ecs/src/world/reflect.rs @@ -4,8 +4,8 @@ use core::any::TypeId; use thiserror::Error; -use alloc::string::{String, ToString}; use bevy_reflect::{Reflect, ReflectFromPtr}; +use bevy_utils::prelude::DebugName; use crate::{prelude::*, world::ComponentId}; @@ -77,10 +77,7 @@ impl World { }; let Some(comp_ptr) = self.get_by_id(entity, component_id) else { - let component_name = self - .components() - .get_name(component_id) - .map(|name| name.to_string()); + let component_name = self.components().get_name(component_id); return Err(GetComponentReflectError::EntityDoesNotHaveComponent { entity, @@ -166,10 +163,7 @@ impl World { // HACK: Only required for the `None`-case/`else`-branch, but it borrows `self`, which will // already be mutably borrowed by `self.get_mut_by_id()`, and I didn't find a way around it. - let component_name = self - .components() - .get_name(component_id) - .map(|name| name.to_string()); + let component_name = self.components().get_name(component_id).clone(); let Some(comp_mut_untyped) = self.get_mut_by_id(entity, component_id) else { return Err(GetComponentReflectError::EntityDoesNotHaveComponent { @@ -223,7 +217,7 @@ pub enum GetComponentReflectError { component_id: ComponentId, /// The name corresponding to the [`Component`] with the given [`TypeId`], or `None` /// if not available. - component_name: Option, + component_name: Option, }, /// The [`World`] was missing the [`AppTypeRegistry`] resource. diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index c0b59c57e7..1745471253 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -14,7 +14,7 @@ use crate::{ lifecycle::RemovedComponentEvents, observer::Observers, prelude::Component, - query::{DebugCheckedUnwrap, ReadOnlyQueryData}, + query::{DebugCheckedUnwrap, ReadOnlyQueryData, ReleaseStateQueryData}, resource::Resource, storage::{ComponentSparseSet, Storages, Table}, world::RawCommandQueue, @@ -1005,7 +1005,9 @@ impl<'w> UnsafeEntityCell<'w> { /// It is the caller's responsibility to ensure that /// - the [`UnsafeEntityCell`] has permission to access the queried data immutably /// - no mutable references to the queried data exist at the same time - pub(crate) unsafe fn get_components(&self) -> Option> { + pub(crate) unsafe fn get_components( + &self, + ) -> Option> { // SAFETY: World is only used to access query data and initialize query state let state = unsafe { let world = self.world().world(); @@ -1035,7 +1037,8 @@ impl<'w> UnsafeEntityCell<'w> { // Table corresponds to archetype. State is the same state used to init fetch above. unsafe { Q::set_archetype(&mut fetch, &state, archetype, table) } // SAFETY: Called after set_archetype above. Entity and location are guaranteed to exist. - unsafe { Some(Q::fetch(&mut fetch, self.id(), location.table_row)) } + let item = unsafe { Q::fetch(&state, &mut fetch, self.id(), location.table_row) }; + Some(Q::release_state(item)) } else { None } diff --git a/crates/bevy_gizmos/src/lib.rs b/crates/bevy_gizmos/src/lib.rs index de48d94e42..5804729e06 100755 --- a/crates/bevy_gizmos/src/lib.rs +++ b/crates/bevy_gizmos/src/lib.rs @@ -631,8 +631,8 @@ impl RenderCommand

for SetLineGizmoBindGroup #[inline] fn render<'w>( _item: &P, - _view: ROQueryItem<'w, Self::ViewQuery>, - uniform_index: Option>, + _view: ROQueryItem<'w, '_, Self::ViewQuery>, + uniform_index: Option>, bind_group: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { @@ -662,8 +662,8 @@ impl RenderCommand

for DrawLineGizmo #[inline] fn render<'w>( _item: &P, - _view: ROQueryItem<'w, Self::ViewQuery>, - config: Option>, + _view: ROQueryItem<'w, '_, Self::ViewQuery>, + config: Option>, line_gizmos: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { @@ -725,8 +725,8 @@ impl RenderCommand

for DrawLineJointGizmo { #[inline] fn render<'w>( _item: &P, - _view: ROQueryItem<'w, Self::ViewQuery>, - config: Option>, + _view: ROQueryItem<'w, '_, Self::ViewQuery>, + config: Option>, line_gizmos: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { diff --git a/crates/bevy_gltf/src/convert_coordinates.rs b/crates/bevy_gltf/src/convert_coordinates.rs new file mode 100644 index 0000000000..4148cecd9a --- /dev/null +++ b/crates/bevy_gltf/src/convert_coordinates.rs @@ -0,0 +1,80 @@ +use core::f32::consts::PI; + +use bevy_math::{Mat4, Quat, Vec3}; +use bevy_transform::components::Transform; + +pub(crate) trait ConvertCoordinates { + /// Converts the glTF coordinates to Bevy's coordinate system. + /// - glTF: + /// - forward: Z + /// - up: Y + /// - right: -X + /// - Bevy: + /// - forward: -Z + /// - up: Y + /// - right: X + /// + /// See + fn convert_coordinates(self) -> Self; +} + +pub(crate) trait ConvertCameraCoordinates { + /// Like `convert_coordinates`, but uses the following for the lens rotation: + /// - forward: -Z + /// - up: Y + /// - right: X + /// + /// See + fn convert_camera_coordinates(self) -> Self; +} + +impl ConvertCoordinates for Vec3 { + fn convert_coordinates(self) -> Self { + Vec3::new(-self.x, self.y, -self.z) + } +} + +impl ConvertCoordinates for [f32; 3] { + fn convert_coordinates(self) -> Self { + [-self[0], self[1], -self[2]] + } +} + +impl ConvertCoordinates for [f32; 4] { + fn convert_coordinates(self) -> Self { + // Solution of q' = r q r* + [-self[0], self[1], -self[2], self[3]] + } +} + +impl ConvertCoordinates for Quat { + fn convert_coordinates(self) -> Self { + // Solution of q' = r q r* + Quat::from_array([-self.x, self.y, -self.z, self.w]) + } +} + +impl ConvertCoordinates for Mat4 { + fn convert_coordinates(self) -> Self { + let m: Mat4 = Mat4::from_scale(Vec3::new(-1.0, 1.0, -1.0)); + // Same as the original matrix + let m_inv = m; + m_inv * self * m + } +} + +impl ConvertCoordinates for Transform { + fn convert_coordinates(mut self) -> Self { + self.translation = self.translation.convert_coordinates(); + self.rotation = self.rotation.convert_coordinates(); + self + } +} + +impl ConvertCameraCoordinates for Transform { + fn convert_camera_coordinates(mut self) -> Self { + self.translation = self.translation.convert_coordinates(); + self.rotate_y(PI); + self + } +} diff --git a/crates/bevy_gltf/src/lib.rs b/crates/bevy_gltf/src/lib.rs index 97600f3ed0..4262d43eb7 100644 --- a/crates/bevy_gltf/src/lib.rs +++ b/crates/bevy_gltf/src/lib.rs @@ -91,6 +91,7 @@ //! You can use [`GltfAssetLabel`] to ensure you are using the correct label. mod assets; +mod convert_coordinates; mod label; mod loader; mod vertex_attributes; diff --git a/crates/bevy_gltf/src/loader/gltf_ext/scene.rs b/crates/bevy_gltf/src/loader/gltf_ext/scene.rs index 83e6778b99..3fce51d527 100644 --- a/crates/bevy_gltf/src/loader/gltf_ext/scene.rs +++ b/crates/bevy_gltf/src/loader/gltf_ext/scene.rs @@ -10,7 +10,10 @@ use itertools::Itertools; #[cfg(feature = "bevy_animation")] use bevy_platform::collections::{HashMap, HashSet}; -use crate::GltfError; +use crate::{ + convert_coordinates::{ConvertCameraCoordinates as _, ConvertCoordinates as _}, + GltfError, +}; pub(crate) fn node_name(node: &Node) -> Name { let name = node @@ -26,8 +29,8 @@ pub(crate) fn node_name(node: &Node) -> Name { /// on [`Node::transform()`](gltf::Node::transform) directly because it uses optimized glam types and /// if `libm` feature of `bevy_math` crate is enabled also handles cross /// platform determinism properly. -pub(crate) fn node_transform(node: &Node) -> Transform { - match node.transform() { +pub(crate) fn node_transform(node: &Node, convert_coordinates: bool) -> Transform { + let transform = match node.transform() { gltf::scene::Transform::Matrix { matrix } => { Transform::from_matrix(Mat4::from_cols_array_2d(&matrix)) } @@ -40,6 +43,15 @@ pub(crate) fn node_transform(node: &Node) -> Transform { rotation: bevy_math::Quat::from_array(rotation), scale: Vec3::from(scale), }, + }; + if convert_coordinates { + if node.camera().is_some() { + transform.convert_camera_coordinates() + } else { + transform.convert_coordinates() + } + } else { + transform } } diff --git a/crates/bevy_gltf/src/loader/mod.rs b/crates/bevy_gltf/src/loader/mod.rs index abc555002a..5e0f752e50 100644 --- a/crates/bevy_gltf/src/loader/mod.rs +++ b/crates/bevy_gltf/src/loader/mod.rs @@ -84,6 +84,7 @@ use self::{ texture::{texture_handle, texture_sampler, texture_transform_to_affine2}, }, }; +use crate::convert_coordinates::ConvertCoordinates as _; /// An error that occurs when loading a glTF file. #[derive(Error, Debug)] @@ -191,6 +192,16 @@ pub struct GltfLoaderSettings { pub default_sampler: Option, /// If true, the loader will ignore sampler data from gltf and use the default sampler. pub override_sampler: bool, + /// If true, the loader will convert glTF coordinates to Bevy's coordinate system. + /// - glTF: + /// - forward: Z + /// - up: Y + /// - right: -X + /// - Bevy: + /// - forward: -Z + /// - up: Y + /// - right: X + pub convert_coordinates: bool, } impl Default for GltfLoaderSettings { @@ -203,6 +214,7 @@ impl Default for GltfLoaderSettings { include_source: false, default_sampler: None, override_sampler: false, + convert_coordinates: false, } } } @@ -303,7 +315,16 @@ async fn load_gltf<'a, 'b, 'c>( match outputs { ReadOutputs::Translations(tr) => { let translation_property = animated_field!(Transform::translation); - let translations: Vec = tr.map(Vec3::from).collect(); + let translations: Vec = tr + .map(Vec3::from) + .map(|verts| { + if settings.convert_coordinates { + Vec3::convert_coordinates(verts) + } else { + verts + } + }) + .collect(); if keyframe_timestamps.len() == 1 { Some(VariableCurve::new(AnimatableCurve::new( translation_property, @@ -350,8 +371,17 @@ async fn load_gltf<'a, 'b, 'c>( } ReadOutputs::Rotations(rots) => { let rotation_property = animated_field!(Transform::rotation); - let rotations: Vec = - rots.into_f32().map(Quat::from_array).collect(); + let rotations: Vec = rots + .into_f32() + .map(Quat::from_array) + .map(|quat| { + if settings.convert_coordinates { + Quat::convert_coordinates(quat) + } else { + quat + } + }) + .collect(); if keyframe_timestamps.len() == 1 { Some(VariableCurve::new(AnimatableCurve::new( rotation_property, @@ -633,6 +663,7 @@ async fn load_gltf<'a, 'b, 'c>( accessor, &buffer_data, &loader.custom_vertex_attributes, + settings.convert_coordinates, ) { Ok((attribute, values)) => mesh.insert_attribute(attribute, values), Err(err) => warn!("{}", err), @@ -752,7 +783,17 @@ async fn load_gltf<'a, 'b, 'c>( let reader = gltf_skin.reader(|buffer| Some(&buffer_data[buffer.index()])); let local_to_bone_bind_matrices: Vec = reader .read_inverse_bind_matrices() - .map(|mats| mats.map(|mat| Mat4::from_cols_array_2d(&mat)).collect()) + .map(|mats| { + mats.map(|mat| Mat4::from_cols_array_2d(&mat)) + .map(|mat| { + if settings.convert_coordinates { + mat.convert_coordinates() + } else { + mat + } + }) + .collect() + }) .unwrap_or_else(|| { core::iter::repeat_n(Mat4::IDENTITY, gltf_skin.joints().len()).collect() }); @@ -834,7 +875,7 @@ async fn load_gltf<'a, 'b, 'c>( &node, children, mesh, - node_transform(&node), + node_transform(&node, settings.convert_coordinates), skin, node.extras().as_deref().map(GltfExtras::from), ); @@ -1306,7 +1347,7 @@ fn load_node( document: &Document, ) -> Result<(), GltfError> { let mut gltf_error = None; - let transform = node_transform(gltf_node); + let transform = node_transform(gltf_node, settings.convert_coordinates); let world_transform = *parent_transform * transform; // according to https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#instantiation, // if the determinant of the transform is negative we must invert the winding order of @@ -1359,7 +1400,6 @@ fn load_node( }, ..OrthographicProjection::default_3d() }; - Projection::Orthographic(orthographic_projection) } gltf::camera::Projection::Perspective(perspective) => { @@ -1377,6 +1417,7 @@ fn load_node( Projection::Perspective(perspective_projection) } }; + node.insert(( Camera3d::default(), projection, diff --git a/crates/bevy_gltf/src/vertex_attributes.rs b/crates/bevy_gltf/src/vertex_attributes.rs index d4ae811c90..2620d608a0 100644 --- a/crates/bevy_gltf/src/vertex_attributes.rs +++ b/crates/bevy_gltf/src/vertex_attributes.rs @@ -6,6 +6,8 @@ use gltf::{ }; use thiserror::Error; +use crate::convert_coordinates::ConvertCoordinates; + /// Represents whether integer data requires normalization #[derive(Copy, Clone)] struct Normalization(bool); @@ -132,15 +134,23 @@ impl<'a> VertexAttributeIter<'a> { } /// Materializes values for any supported format of vertex attribute - fn into_any_values(self) -> Result { + fn into_any_values(self, convert_coordinates: bool) -> Result { match self { VertexAttributeIter::F32(it) => Ok(Values::Float32(it.collect())), VertexAttributeIter::U32(it) => Ok(Values::Uint32(it.collect())), VertexAttributeIter::F32x2(it) => Ok(Values::Float32x2(it.collect())), VertexAttributeIter::U32x2(it) => Ok(Values::Uint32x2(it.collect())), - VertexAttributeIter::F32x3(it) => Ok(Values::Float32x3(it.collect())), + VertexAttributeIter::F32x3(it) => Ok(if convert_coordinates { + Values::Float32x3(it.map(ConvertCoordinates::convert_coordinates).collect()) + } else { + Values::Float32x3(it.collect()) + }), VertexAttributeIter::U32x3(it) => Ok(Values::Uint32x3(it.collect())), - VertexAttributeIter::F32x4(it) => Ok(Values::Float32x4(it.collect())), + VertexAttributeIter::F32x4(it) => Ok(if convert_coordinates { + Values::Float32x4(it.map(ConvertCoordinates::convert_coordinates).collect()) + } else { + Values::Float32x4(it.collect()) + }), VertexAttributeIter::U32x4(it) => Ok(Values::Uint32x4(it.collect())), VertexAttributeIter::S16x2(it, n) => { Ok(n.apply_either(it.collect(), Values::Snorm16x2, Values::Sint16x2)) @@ -188,7 +198,7 @@ impl<'a> VertexAttributeIter<'a> { VertexAttributeIter::U16x4(it, Normalization(true)) => Ok(Values::Float32x4( ReadColors::RgbaU16(it).into_rgba_f32().collect(), )), - s => s.into_any_values(), + s => s.into_any_values(false), } } @@ -198,7 +208,7 @@ impl<'a> VertexAttributeIter<'a> { VertexAttributeIter::U8x4(it, Normalization(false)) => { Ok(Values::Uint16x4(ReadJoints::U8(it).into_u16().collect())) } - s => s.into_any_values(), + s => s.into_any_values(false), } } @@ -211,7 +221,7 @@ impl<'a> VertexAttributeIter<'a> { VertexAttributeIter::U16x4(it, Normalization(true)) => { Ok(Values::Float32x4(ReadWeights::U16(it).into_f32().collect())) } - s => s.into_any_values(), + s => s.into_any_values(false), } } @@ -224,7 +234,7 @@ impl<'a> VertexAttributeIter<'a> { VertexAttributeIter::U16x2(it, Normalization(true)) => Ok(Values::Float32x2( ReadTexCoords::U16(it).into_f32().collect(), )), - s => s.into_any_values(), + s => s.into_any_values(false), } } } @@ -252,28 +262,49 @@ pub(crate) fn convert_attribute( accessor: gltf::Accessor, buffer_data: &Vec>, custom_vertex_attributes: &HashMap, MeshVertexAttribute>, + convert_coordinates: bool, ) -> Result<(MeshVertexAttribute, Values), ConvertAttributeError> { - if let Some((attribute, conversion)) = match &semantic { - gltf::Semantic::Positions => Some((Mesh::ATTRIBUTE_POSITION, ConversionMode::Any)), - gltf::Semantic::Normals => Some((Mesh::ATTRIBUTE_NORMAL, ConversionMode::Any)), - gltf::Semantic::Tangents => Some((Mesh::ATTRIBUTE_TANGENT, ConversionMode::Any)), - gltf::Semantic::Colors(0) => Some((Mesh::ATTRIBUTE_COLOR, ConversionMode::Rgba)), - gltf::Semantic::TexCoords(0) => Some((Mesh::ATTRIBUTE_UV_0, ConversionMode::TexCoord)), - gltf::Semantic::TexCoords(1) => Some((Mesh::ATTRIBUTE_UV_1, ConversionMode::TexCoord)), - gltf::Semantic::Joints(0) => { - Some((Mesh::ATTRIBUTE_JOINT_INDEX, ConversionMode::JointIndex)) + if let Some((attribute, conversion, convert_coordinates)) = match &semantic { + gltf::Semantic::Positions => Some(( + Mesh::ATTRIBUTE_POSITION, + ConversionMode::Any, + convert_coordinates, + )), + gltf::Semantic::Normals => Some(( + Mesh::ATTRIBUTE_NORMAL, + ConversionMode::Any, + convert_coordinates, + )), + gltf::Semantic::Tangents => Some(( + Mesh::ATTRIBUTE_TANGENT, + ConversionMode::Any, + convert_coordinates, + )), + gltf::Semantic::Colors(0) => Some((Mesh::ATTRIBUTE_COLOR, ConversionMode::Rgba, false)), + gltf::Semantic::TexCoords(0) => { + Some((Mesh::ATTRIBUTE_UV_0, ConversionMode::TexCoord, false)) } - gltf::Semantic::Weights(0) => { - Some((Mesh::ATTRIBUTE_JOINT_WEIGHT, ConversionMode::JointWeight)) + gltf::Semantic::TexCoords(1) => { + Some((Mesh::ATTRIBUTE_UV_1, ConversionMode::TexCoord, false)) } + gltf::Semantic::Joints(0) => Some(( + Mesh::ATTRIBUTE_JOINT_INDEX, + ConversionMode::JointIndex, + false, + )), + gltf::Semantic::Weights(0) => Some(( + Mesh::ATTRIBUTE_JOINT_WEIGHT, + ConversionMode::JointWeight, + false, + )), gltf::Semantic::Extras(name) => custom_vertex_attributes .get(name.as_str()) - .map(|attr| (*attr, ConversionMode::Any)), + .map(|attr| (*attr, ConversionMode::Any, false)), _ => None, } { let raw_iter = VertexAttributeIter::from_accessor(accessor.clone(), buffer_data); let converted_values = raw_iter.and_then(|iter| match conversion { - ConversionMode::Any => iter.into_any_values(), + ConversionMode::Any => iter.into_any_values(convert_coordinates), ConversionMode::Rgba => iter.into_rgba_values(), ConversionMode::TexCoord => iter.into_tex_coord_values(), ConversionMode::JointIndex => iter.into_joint_index_values(), diff --git a/crates/bevy_input/src/button_input.rs b/crates/bevy_input/src/button_input.rs index 58ab62aefa..e4ff47f470 100644 --- a/crates/bevy_input/src/button_input.rs +++ b/crates/bevy_input/src/button_input.rs @@ -122,7 +122,7 @@ use { /// [`DetectChangesMut::bypass_change_detection`]: bevy_ecs::change_detection::DetectChangesMut::bypass_change_detection #[derive(Debug, Clone, Resource)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Default, Resource))] -pub struct ButtonInput { +pub struct ButtonInput { /// A collection of every button that is currently being pressed. pressed: HashSet, /// A collection of every button that has just been pressed. @@ -131,7 +131,7 @@ pub struct ButtonInput { just_released: HashSet, } -impl Default for ButtonInput { +impl Default for ButtonInput { fn default() -> Self { Self { pressed: Default::default(), @@ -143,12 +143,12 @@ impl Default for ButtonInput { impl ButtonInput where - T: Copy + Eq + Hash + Send + Sync + 'static, + T: Clone + Eq + Hash + Send + Sync + 'static, { /// Registers a press for the given `input`. pub fn press(&mut self, input: T) { // Returns `true` if the `input` wasn't pressed. - if self.pressed.insert(input) { + if self.pressed.insert(input.clone()) { self.just_pressed.insert(input); } } diff --git a/crates/bevy_input/src/keyboard.rs b/crates/bevy_input/src/keyboard.rs index 909880ac7a..70efe18a84 100644 --- a/crates/bevy_input/src/keyboard.rs +++ b/crates/bevy_input/src/keyboard.rs @@ -92,8 +92,9 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; /// /// ## Usage /// -/// The event is consumed inside of the [`keyboard_input_system`] -/// to update the [`ButtonInput`](ButtonInput) resource. +/// The event is consumed inside of the [`keyboard_input_system`] to update the +/// [`ButtonInput`](ButtonInput) and +/// [`ButtonInput`](ButtonInput) resources. #[derive(Event, BufferedEvent, Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr( feature = "bevy_reflect", @@ -107,8 +108,12 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; )] pub struct KeyboardInput { /// The physical key code of the key. + /// + /// This corresponds to the location of the key independent of the keyboard layout. pub key_code: KeyCode, - /// The logical key of the input + /// The logical key of the input. + /// + /// This corresponds to the actual key taking keyboard layout into account. pub logical_key: Key, /// The press state of the key. pub state: ButtonState, @@ -148,32 +153,46 @@ pub struct KeyboardInput { )] pub struct KeyboardFocusLost; -/// Updates the [`ButtonInput`] resource with the latest [`KeyboardInput`] events. +/// Updates the [`ButtonInput`] and [`ButtonInput`] resources with the latest [`KeyboardInput`] events. /// /// ## Differences /// -/// The main difference between the [`KeyboardInput`] event and the [`ButtonInput`] resources is that +/// The main difference between the [`KeyboardInput`] event and the [`ButtonInput`] resources are that /// the latter has convenient functions such as [`ButtonInput::pressed`], [`ButtonInput::just_pressed`] and [`ButtonInput::just_released`] and is window id agnostic. +/// +/// There is a [`ButtonInput`] for both [`KeyCode`] and [`Key`] as they are both useful in different situations, see their documentation for the details. pub fn keyboard_input_system( - mut key_input: ResMut>, + mut keycode_input: ResMut>, + mut key_input: ResMut>, mut keyboard_input_events: EventReader, mut focus_events: EventReader, ) { - // Avoid clearing if it's not empty to ensure change detection is not triggered. + // Avoid clearing if not empty to ensure change detection is not triggered. + keycode_input.bypass_change_detection().clear(); key_input.bypass_change_detection().clear(); + for event in keyboard_input_events.read() { let KeyboardInput { - key_code, state, .. + key_code, + logical_key, + state, + .. } = event; match state { - ButtonState::Pressed => key_input.press(*key_code), - ButtonState::Released => key_input.release(*key_code), + ButtonState::Pressed => { + keycode_input.press(*key_code); + key_input.press(logical_key.clone()); + } + ButtonState::Released => { + keycode_input.release(*key_code); + key_input.release(logical_key.clone()); + } } } // Release all cached input to avoid having stuck input when switching between windows in os if !focus_events.is_empty() { - key_input.release_all(); + keycode_input.release_all(); focus_events.clear(); } } @@ -220,13 +239,13 @@ pub enum NativeKeyCode { /// It is used as the generic `T` value of an [`ButtonInput`] to create a `Res>`. /// /// Code representing the location of a physical key -/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.code`] with a few +/// This mostly conforms to the [`UI Events Specification's KeyboardEvent.code`] with a few /// exceptions: /// - The keys that the specification calls `MetaLeft` and `MetaRight` are named `SuperLeft` and /// `SuperRight` here. /// - The key that the specification calls "Super" is reported as `Unidentified` here. /// -/// [`KeyboardEvent.code`]: https://w3c.github.io/uievents-code/#code-value-tables +/// [`UI Events Specification's KeyboardEvent.code`]: https://w3c.github.io/uievents-code/#code-value-tables /// /// ## Updating /// @@ -756,6 +775,19 @@ pub enum NativeKey { /// The logical key code of a [`KeyboardInput`]. /// +/// This contains the actual value that is produced by pressing the key. This is +/// useful when you need the actual letters, and for symbols like `+` and `-` +/// when implementing zoom, as they can be in different locations depending on +/// the keyboard layout. +/// +/// In many cases you want the key location instead, for example when +/// implementing WASD controls so the keys are located the same place on QWERTY +/// and other layouts. In that case use [`KeyCode`] instead. +/// +/// ## Usage +/// +/// It is used as the generic `T` value of an [`ButtonInput`] to create a `Res>`. +/// /// ## Technical /// /// Its values map 1 to 1 to winit's Key. diff --git a/crates/bevy_input/src/lib.rs b/crates/bevy_input/src/lib.rs index 653af4c991..5a6a177223 100644 --- a/crates/bevy_input/src/lib.rs +++ b/crates/bevy_input/src/lib.rs @@ -49,7 +49,7 @@ use bevy_ecs::prelude::*; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; use gestures::*; -use keyboard::{keyboard_input_system, KeyCode, KeyboardFocusLost, KeyboardInput}; +use keyboard::{keyboard_input_system, Key, KeyCode, KeyboardFocusLost, KeyboardInput}; use mouse::{ accumulate_mouse_motion_system, accumulate_mouse_scroll_system, mouse_button_input_system, AccumulatedMouseMotion, AccumulatedMouseScroll, MouseButton, MouseButtonInput, MouseMotion, @@ -89,6 +89,7 @@ impl Plugin for InputPlugin { .add_event::() .add_event::() .init_resource::>() + .init_resource::>() .add_systems(PreUpdate, keyboard_input_system.in_set(InputSystems)) // mouse .add_event::() diff --git a/crates/bevy_input_focus/src/lib.rs b/crates/bevy_input_focus/src/lib.rs index 1fe7c4b7ef..cbf88740fd 100644 --- a/crates/bevy_input_focus/src/lib.rs +++ b/crates/bevy_input_focus/src/lib.rs @@ -155,7 +155,7 @@ pub struct WindowTraversal { } impl Traversal> for WindowTraversal { - fn traverse(item: Self::Item<'_>, event: &FocusedInput) -> Option { + fn traverse(item: Self::Item<'_, '_>, event: &FocusedInput) -> Option { let WindowTraversalItem { child_of, window } = item; // Send event to parent, if it has one. diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index 54b9d7ffc2..c9639f1950 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -346,6 +346,8 @@ web = [ hotpatching = ["bevy_app/hotpatching", "bevy_ecs/hotpatching"] +debug = ["bevy_utils/debug"] + [dependencies] # bevy (no_std) bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [ diff --git a/crates/bevy_pbr/src/atmosphere/mod.rs b/crates/bevy_pbr/src/atmosphere/mod.rs index 8b08751428..a55403630a 100644 --- a/crates/bevy_pbr/src/atmosphere/mod.rs +++ b/crates/bevy_pbr/src/atmosphere/mod.rs @@ -309,7 +309,7 @@ impl ExtractComponent for Atmosphere { type Out = Atmosphere; - fn extract_component(item: QueryItem<'_, Self::QueryData>) -> Option { + fn extract_component(item: QueryItem<'_, '_, Self::QueryData>) -> Option { Some(item.clone()) } } @@ -405,7 +405,7 @@ impl ExtractComponent for AtmosphereSettings { type Out = AtmosphereSettings; - fn extract_component(item: QueryItem<'_, Self::QueryData>) -> Option { + fn extract_component(item: QueryItem<'_, '_, Self::QueryData>) -> Option { Some(item.clone()) } } diff --git a/crates/bevy_pbr/src/atmosphere/node.rs b/crates/bevy_pbr/src/atmosphere/node.rs index 851447d760..e09b27c590 100644 --- a/crates/bevy_pbr/src/atmosphere/node.rs +++ b/crates/bevy_pbr/src/atmosphere/node.rs @@ -181,7 +181,7 @@ impl ViewNode for RenderSkyNode { view_uniforms_offset, lights_uniforms_offset, render_sky_pipeline_id, - ): QueryItem<'w, Self::ViewQuery>, + ): QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { let pipeline_cache = world.resource::(); diff --git a/crates/bevy_pbr/src/atmosphere/resources.rs b/crates/bevy_pbr/src/atmosphere/resources.rs index 8c57fb8eb9..d7c93c4418 100644 --- a/crates/bevy_pbr/src/atmosphere/resources.rs +++ b/crates/bevy_pbr/src/atmosphere/resources.rs @@ -560,7 +560,7 @@ pub(super) fn prepare_atmosphere_transforms( }; for (entity, view) in &views { - let world_from_view = view.world_from_view.compute_matrix(); + let world_from_view = view.world_from_view.to_matrix(); let camera_z = world_from_view.z_axis.truncate(); let camera_y = world_from_view.y_axis.truncate(); let atmo_z = camera_z diff --git a/crates/bevy_pbr/src/cluster/assign.rs b/crates/bevy_pbr/src/cluster/assign.rs index 1b7b3563d7..b0c0fb6347 100644 --- a/crates/bevy_pbr/src/cluster/assign.rs +++ b/crates/bevy_pbr/src/cluster/assign.rs @@ -353,7 +353,7 @@ pub(crate) fn assign_objects_to_clusters( let mut requested_cluster_dimensions = config.dimensions_for_screen_size(screen_size); - let world_from_view = camera_transform.compute_matrix(); + let world_from_view = camera_transform.to_matrix(); let view_from_world_scale = camera_transform.compute_transform().scale.recip(); let view_from_world_scale_max = view_from_world_scale.abs().max_element(); let view_from_world = world_from_view.inverse(); diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 4ff4662bb2..8273ae4b6d 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -341,7 +341,7 @@ pub fn build_directional_light_cascades( .iter() .filter_map(|(entity, transform, projection, camera)| { if camera.is_active { - Some((entity, projection, transform.compute_matrix())) + Some((entity, projection, transform.to_matrix())) } else { None } @@ -357,7 +357,7 @@ pub fn build_directional_light_cascades( // light_to_world has orthogonal upper-left 3x3 and zero translation. // Even though only the direction (i.e. rotation) of the light matters, we don't constrain // users to not change any other aspects of the transform - there's no guarantee - // `transform.compute_matrix()` will give us a matrix with our desired properties. + // `transform.to_matrix()` will give us a matrix with our desired properties. // Instead, we directly create a good matrix from just the rotation. let world_from_light = Mat4::from_quat(transform.compute_transform().rotation); let light_to_world_inverse = world_from_light.inverse(); @@ -628,7 +628,7 @@ pub fn update_point_light_frusta( for (view_rotation, frustum) in view_rotations.iter().zip(cubemap_frusta.iter_mut()) { let world_from_view = view_translation * *view_rotation; - let clip_from_world = clip_from_view * world_from_view.compute_matrix().inverse(); + let clip_from_world = clip_from_view * world_from_view.to_matrix().inverse(); *frustum = Frustum::from_clip_from_world_custom_far( &clip_from_world, diff --git a/crates/bevy_pbr/src/light_probe/environment_map.rs b/crates/bevy_pbr/src/light_probe/environment_map.rs index b31801f757..fa55b4d94a 100644 --- a/crates/bevy_pbr/src/light_probe/environment_map.rs +++ b/crates/bevy_pbr/src/light_probe/environment_map.rs @@ -192,7 +192,7 @@ impl ExtractInstance for EnvironmentMapIds { type QueryFilter = (); - fn extract(item: QueryItem<'_, Self::QueryData>) -> Option { + fn extract(item: QueryItem<'_, '_, Self::QueryData>) -> Option { Some(EnvironmentMapIds { diffuse: item.diffuse_map.id(), specular: item.specular_map.id(), diff --git a/crates/bevy_pbr/src/light_probe/mod.rs b/crates/bevy_pbr/src/light_probe/mod.rs index 74710ce1d5..bfce2f1e26 100644 --- a/crates/bevy_pbr/src/light_probe/mod.rs +++ b/crates/bevy_pbr/src/light_probe/mod.rs @@ -378,7 +378,7 @@ fn gather_environment_map_uniform( let environment_map_uniform = if let Some(environment_map_light) = environment_map_light { EnvironmentMapUniform { transform: Transform::from_rotation(environment_map_light.rotation) - .compute_matrix() + .to_matrix() .inverse(), } } else { @@ -595,7 +595,7 @@ where ) -> Option> { environment_map.id(image_assets).map(|id| LightProbeInfo { world_from_light: light_probe_transform.affine(), - light_from_world: light_probe_transform.compute_matrix().inverse(), + light_from_world: light_probe_transform.to_matrix().inverse(), asset_id: id, intensity: environment_map.intensity(), affects_lightmapped_mesh_diffuse: environment_map.affects_lightmapped_mesh_diffuse(), diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 1ae0c7fa84..aef2b74177 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -216,7 +216,7 @@ pub fn update_previous_view_data( query: Query<(Entity, &Camera, &GlobalTransform), Or<(With, With)>>, ) { for (entity, camera, camera_transform) in &query { - let world_from_view = camera_transform.compute_matrix(); + let world_from_view = camera_transform.to_matrix(); let view_from_world = world_from_view.inverse(); let view_from_clip = camera.clip_from_view().inverse(); @@ -703,7 +703,7 @@ pub fn prepare_previous_view_uniforms( let prev_view_data = match maybe_previous_view_uniforms { Some(previous_view) => previous_view.clone(), None => { - let world_from_view = camera.world_from_view.compute_matrix(); + let world_from_view = camera.world_from_view.to_matrix(); let view_from_world = world_from_view.inverse(); let view_from_clip = camera.clip_from_view.inverse(); diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 8c408233e1..d8a3256b13 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -1540,7 +1540,7 @@ fn extract_mesh_for_gpu_building( not_shadow_caster, no_automatic_batching, visibility_range, - ): ::Item<'_>, + ): ::Item<'_, '_>, render_visibility_ranges: &RenderVisibilityRanges, render_mesh_instances: &RenderMeshInstancesGpu, queue: &mut RenderMeshInstanceGpuQueue, @@ -2874,7 +2874,7 @@ impl RenderCommand

for SetMeshViewBindGroup view_environment_map, mesh_view_bind_group, maybe_oit_layers_count_offset, - ): ROQueryItem<'w, Self::ViewQuery>, + ): ROQueryItem<'w, '_, Self::ViewQuery>, _entity: Option<()>, _: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, diff --git a/crates/bevy_pbr/src/ssr/mod.rs b/crates/bevy_pbr/src/ssr/mod.rs index 9f7dbb2f76..22d5acd1e9 100644 --- a/crates/bevy_pbr/src/ssr/mod.rs +++ b/crates/bevy_pbr/src/ssr/mod.rs @@ -280,7 +280,7 @@ impl ViewNode for ScreenSpaceReflectionsNode { view_environment_map_offset, view_bind_group, ssr_pipeline_id, - ): QueryItem<'w, Self::ViewQuery>, + ): QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { // Grab the render pipeline. @@ -498,7 +498,7 @@ impl ExtractComponent for ScreenSpaceReflections { type Out = ScreenSpaceReflectionsUniform; - fn extract_component(settings: QueryItem<'_, Self::QueryData>) -> Option { + fn extract_component(settings: QueryItem<'_, '_, Self::QueryData>) -> Option { if !DEPTH_TEXTURE_SAMPLING_SUPPORTED { once!(info!( "Disabling screen-space reflections on this platform because depth textures \ diff --git a/crates/bevy_pbr/src/volumetric_fog/render.rs b/crates/bevy_pbr/src/volumetric_fog/render.rs index 45f694e546..cf2989a980 100644 --- a/crates/bevy_pbr/src/volumetric_fog/render.rs +++ b/crates/bevy_pbr/src/volumetric_fog/render.rs @@ -347,7 +347,7 @@ impl ViewNode for VolumetricFogNode { view_ssr_offset, msaa, view_environment_map_offset, - ): QueryItem<'w, Self::ViewQuery>, + ): QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { let pipeline_cache = world.resource::(); @@ -700,7 +700,7 @@ pub fn prepare_volumetric_fog_uniforms( // Do this up front to avoid O(n^2) matrix inversion. local_from_world_matrices.clear(); for (_, _, fog_transform) in fog_volumes.iter() { - local_from_world_matrices.push(fog_transform.compute_matrix().inverse()); + local_from_world_matrices.push(fog_transform.to_matrix().inverse()); } let uniform_count = view_targets.iter().len() * local_from_world_matrices.len(); @@ -712,7 +712,7 @@ pub fn prepare_volumetric_fog_uniforms( }; for (view_entity, extracted_view, volumetric_fog) in view_targets.iter() { - let world_from_view = extracted_view.world_from_view.compute_matrix(); + let world_from_view = extracted_view.world_from_view.to_matrix(); let mut view_fog_volumes = vec![]; diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index cc64ad2e4f..e42e1309ec 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -374,7 +374,7 @@ impl ViewNode for Wireframe3dNode { &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext<'w>, - (camera, view, target, depth): QueryItem<'w, Self::ViewQuery>, + (camera, view, target, depth): QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { let Some(wireframe_phase) = world.get_resource::>() diff --git a/crates/bevy_picking/src/events.rs b/crates/bevy_picking/src/events.rs index 2116d986af..a7a3979c59 100644 --- a/crates/bevy_picking/src/events.rs +++ b/crates/bevy_picking/src/events.rs @@ -86,7 +86,7 @@ impl Traversal> for PointerTraversal where E: Debug + Clone + Reflect, { - fn traverse(item: Self::Item<'_>, pointer: &Pointer) -> Option { + fn traverse(item: Self::Item<'_, '_>, pointer: &Pointer) -> Option { let PointerTraversalItem { child_of, window } = item; // Send event to parent, if it has one. diff --git a/crates/bevy_picking/src/mesh_picking/ray_cast/intersections.rs b/crates/bevy_picking/src/mesh_picking/ray_cast/intersections.rs index 9988a96e19..5ac2d89887 100644 --- a/crates/bevy_picking/src/mesh_picking/ray_cast/intersections.rs +++ b/crates/bevy_picking/src/mesh_picking/ray_cast/intersections.rs @@ -317,7 +317,7 @@ mod tests { #[test] fn ray_mesh_intersection_simple() { let ray = Ray3d::new(Vec3::ZERO, Dir3::X); - let mesh_transform = GlobalTransform::IDENTITY.compute_matrix(); + let mesh_transform = GlobalTransform::IDENTITY.to_matrix(); let positions = &[V0, V1, V2]; let vertex_normals = None; let indices: Option<&[u16]> = None; @@ -338,7 +338,7 @@ mod tests { #[test] fn ray_mesh_intersection_indices() { let ray = Ray3d::new(Vec3::ZERO, Dir3::X); - let mesh_transform = GlobalTransform::IDENTITY.compute_matrix(); + let mesh_transform = GlobalTransform::IDENTITY.to_matrix(); let positions = &[V0, V1, V2]; let vertex_normals = None; let indices: Option<&[u16]> = Some(&[0, 1, 2]); @@ -359,7 +359,7 @@ mod tests { #[test] fn ray_mesh_intersection_indices_vertex_normals() { let ray = Ray3d::new(Vec3::ZERO, Dir3::X); - let mesh_transform = GlobalTransform::IDENTITY.compute_matrix(); + let mesh_transform = GlobalTransform::IDENTITY.to_matrix(); let positions = &[V0, V1, V2]; let vertex_normals: Option<&[[f32; 3]]> = Some(&[[-1., 0., 0.], [-1., 0., 0.], [-1., 0., 0.]]); @@ -381,7 +381,7 @@ mod tests { #[test] fn ray_mesh_intersection_vertex_normals() { let ray = Ray3d::new(Vec3::ZERO, Dir3::X); - let mesh_transform = GlobalTransform::IDENTITY.compute_matrix(); + let mesh_transform = GlobalTransform::IDENTITY.to_matrix(); let positions = &[V0, V1, V2]; let vertex_normals: Option<&[[f32; 3]]> = Some(&[[-1., 0., 0.], [-1., 0., 0.], [-1., 0., 0.]]); @@ -403,7 +403,7 @@ mod tests { #[test] fn ray_mesh_intersection_missing_vertex_normals() { let ray = Ray3d::new(Vec3::ZERO, Dir3::X); - let mesh_transform = GlobalTransform::IDENTITY.compute_matrix(); + let mesh_transform = GlobalTransform::IDENTITY.to_matrix(); let positions = &[V0, V1, V2]; let vertex_normals: Option<&[[f32; 3]]> = Some(&[]); let indices: Option<&[u16]> = None; @@ -424,7 +424,7 @@ mod tests { #[test] fn ray_mesh_intersection_indices_missing_vertex_normals() { let ray = Ray3d::new(Vec3::ZERO, Dir3::X); - let mesh_transform = GlobalTransform::IDENTITY.compute_matrix(); + let mesh_transform = GlobalTransform::IDENTITY.to_matrix(); let positions = &[V0, V1, V2]; let vertex_normals: Option<&[[f32; 3]]> = Some(&[]); let indices: Option<&[u16]> = Some(&[0, 1, 2]); @@ -445,7 +445,7 @@ mod tests { #[test] fn ray_mesh_intersection_not_enough_indices() { let ray = Ray3d::new(Vec3::ZERO, Dir3::X); - let mesh_transform = GlobalTransform::IDENTITY.compute_matrix(); + let mesh_transform = GlobalTransform::IDENTITY.to_matrix(); let positions = &[V0, V1, V2]; let vertex_normals = None; let indices: Option<&[u16]> = Some(&[0]); @@ -466,7 +466,7 @@ mod tests { #[test] fn ray_mesh_intersection_bad_indices() { let ray = Ray3d::new(Vec3::ZERO, Dir3::X); - let mesh_transform = GlobalTransform::IDENTITY.compute_matrix(); + let mesh_transform = GlobalTransform::IDENTITY.to_matrix(); let positions = &[V0, V1, V2]; let vertex_normals = None; let indices: Option<&[u16]> = Some(&[0, 1, 3]); diff --git a/crates/bevy_picking/src/mesh_picking/ray_cast/mod.rs b/crates/bevy_picking/src/mesh_picking/ray_cast/mod.rs index c1f465b96a..e42dc160e2 100644 --- a/crates/bevy_picking/src/mesh_picking/ray_cast/mod.rs +++ b/crates/bevy_picking/src/mesh_picking/ray_cast/mod.rs @@ -233,7 +233,7 @@ impl<'w, 's> MeshRayCast<'w, 's> { if let Some(distance) = ray_aabb_intersection_3d( ray, &Aabb3d::new(aabb.center, aabb.half_extents), - &transform.compute_matrix(), + &transform.to_matrix(), ) { aabb_hits_tx.send((FloatOrd(distance), entity)).ok(); } @@ -287,7 +287,7 @@ impl<'w, 's> MeshRayCast<'w, 's> { // Perform the actual ray cast. let _ray_cast_guard = ray_cast_guard.enter(); - let transform = transform.compute_matrix(); + let transform = transform.to_matrix(); let intersection = ray_intersection_over_mesh(mesh, &transform, ray, backfaces); if let Some(intersection) = intersection { diff --git a/crates/bevy_reflect/src/array.rs b/crates/bevy_reflect/src/array.rs index 55f62b34c8..df9580b820 100644 --- a/crates/bevy_reflect/src/array.rs +++ b/crates/bevy_reflect/src/array.rs @@ -167,6 +167,7 @@ pub struct DynamicArray { } impl DynamicArray { + /// Creates a new [`DynamicArray`]. #[inline] pub fn new(values: Box<[Box]>) -> Self { Self { diff --git a/crates/bevy_reflect/src/attributes.rs b/crates/bevy_reflect/src/attributes.rs index 728102c4b0..4d8154fad3 100644 --- a/crates/bevy_reflect/src/attributes.rs +++ b/crates/bevy_reflect/src/attributes.rs @@ -1,3 +1,5 @@ +//! Types and functions for creating, manipulating and querying [`CustomAttributes`]. + use crate::Reflect; use alloc::boxed::Box; use bevy_utils::TypeIdMap; @@ -98,16 +100,19 @@ struct CustomAttribute { } impl CustomAttribute { + /// Creates a new [`CustomAttribute`] containing `value`. pub fn new(value: T) -> Self { Self { value: Box::new(value), } } + /// Returns a reference to the attribute's value if it is of type `T`, or [`None`] if not. pub fn value(&self) -> Option<&T> { self.value.downcast_ref() } + /// Returns a reference to the attribute's value as a [`Reflect`] trait object. pub fn reflect_value(&self) -> &dyn Reflect { &*self.value } diff --git a/crates/bevy_reflect/src/enums/dynamic_enum.rs b/crates/bevy_reflect/src/enums/dynamic_enum.rs index 3f0b275519..2835306b22 100644 --- a/crates/bevy_reflect/src/enums/dynamic_enum.rs +++ b/crates/bevy_reflect/src/enums/dynamic_enum.rs @@ -13,9 +13,12 @@ use derive_more::derive::From; /// A dynamic representation of an enum variant. #[derive(Debug, Default, From)] pub enum DynamicVariant { + /// A unit variant. #[default] Unit, + /// A tuple variant. Tuple(DynamicTuple), + /// A struct variant. Struct(DynamicStruct), } diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 126c407f23..32e4b96124 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -263,6 +263,7 @@ pub struct VariantFieldIter<'a> { } impl<'a> VariantFieldIter<'a> { + /// Creates a new [`VariantFieldIter`]. pub fn new(container: &'a dyn Enum) -> Self { Self { container, @@ -295,12 +296,16 @@ impl<'a> Iterator for VariantFieldIter<'a> { impl<'a> ExactSizeIterator for VariantFieldIter<'a> {} +/// A field in the current enum variant. pub enum VariantField<'a> { + /// The name and value of a field in a struct variant. Struct(&'a str, &'a dyn PartialReflect), + /// The value of a field in a tuple variant. Tuple(&'a dyn PartialReflect), } impl<'a> VariantField<'a> { + /// Returns the name of a struct variant field, or [`None`] for a tuple variant field. pub fn name(&self) -> Option<&'a str> { if let Self::Struct(name, ..) = self { Some(*name) @@ -309,6 +314,7 @@ impl<'a> VariantField<'a> { } } + /// Gets a reference to the value of this field. pub fn value(&self) -> &'a dyn PartialReflect { match *self { Self::Struct(_, value) | Self::Tuple(value) => value, diff --git a/crates/bevy_reflect/src/enums/variants.rs b/crates/bevy_reflect/src/enums/variants.rs index 55ccb8efb1..d4fcc2845a 100644 --- a/crates/bevy_reflect/src/enums/variants.rs +++ b/crates/bevy_reflect/src/enums/variants.rs @@ -47,7 +47,9 @@ pub enum VariantInfoError { /// [type]: VariantType #[error("variant type mismatch: expected {expected:?}, received {received:?}")] TypeMismatch { + /// Expected variant type. expected: VariantType, + /// Received variant type. received: VariantType, }, } @@ -84,6 +86,7 @@ pub enum VariantInfo { } impl VariantInfo { + /// The name of the enum variant. pub fn name(&self) -> &'static str { match self { Self::Struct(info) => info.name(), diff --git a/crates/bevy_reflect/src/error.rs b/crates/bevy_reflect/src/error.rs index a13b55cdc0..d8bb8a9e14 100644 --- a/crates/bevy_reflect/src/error.rs +++ b/crates/bevy_reflect/src/error.rs @@ -11,14 +11,20 @@ pub enum ReflectCloneError { /// /// [`PartialReflect::reflect_clone`]: crate::PartialReflect::reflect_clone #[error("`PartialReflect::reflect_clone` not implemented for `{type_path}`")] - NotImplemented { type_path: Cow<'static, str> }, + NotImplemented { + /// The fully qualified path of the type that [`PartialReflect::reflect_clone`](crate::PartialReflect::reflect_clone) is not implemented for. + type_path: Cow<'static, str>, + }, /// The type cannot be cloned via [`PartialReflect::reflect_clone`]. /// /// This type should be returned when a type is intentionally opting out of reflection cloning. /// /// [`PartialReflect::reflect_clone`]: crate::PartialReflect::reflect_clone #[error("`{type_path}` cannot be made cloneable for `PartialReflect::reflect_clone`")] - NotCloneable { type_path: Cow<'static, str> }, + NotCloneable { + /// The fully qualified path of the type that cannot be cloned via [`PartialReflect::reflect_clone`](crate::PartialReflect::reflect_clone). + type_path: Cow<'static, str>, + }, /// The field cannot be cloned via [`PartialReflect::reflect_clone`]. /// /// When [deriving `Reflect`], this usually means that a field marked with `#[reflect(ignore)]` @@ -33,8 +39,11 @@ pub enum ReflectCloneError { full_path(.field, .variant.as_deref(), .container_type_path) )] FieldNotCloneable { + /// Struct field or enum variant field which cannot be cloned. field: FieldId, + /// Variant this field is part of if the container is an enum, otherwise [`None`]. variant: Option>, + /// Fully qualified path of the type containing this field. container_type_path: Cow<'static, str>, }, /// Could not downcast to the expected type. @@ -44,7 +53,9 @@ pub enum ReflectCloneError { /// [`Reflect`]: crate::Reflect #[error("expected downcast to `{expected}`, but received `{received}`")] FailedDowncast { + /// The fully qualified path of the type that was expected. expected: Cow<'static, str>, + /// The fully qualified path of the type that was received. received: Cow<'static, str>, }, } diff --git a/crates/bevy_reflect/src/fields.rs b/crates/bevy_reflect/src/fields.rs index 21d4ccd98a..53223835b3 100644 --- a/crates/bevy_reflect/src/fields.rs +++ b/crates/bevy_reflect/src/fields.rs @@ -82,6 +82,7 @@ pub struct UnnamedField { } impl UnnamedField { + /// Create a new [`UnnamedField`]. pub fn new(index: usize) -> Self { Self { index, @@ -135,7 +136,9 @@ impl UnnamedField { /// A representation of a field's accessor. #[derive(Clone, Debug, PartialEq, Eq)] pub enum FieldId { + /// Access a field by name. Named(Cow<'static, str>), + /// Access a field by index. Unnamed(usize), } diff --git a/crates/bevy_reflect/src/func/args/arg.rs b/crates/bevy_reflect/src/func/args/arg.rs index 8ca03aafd3..1c157a6b2f 100644 --- a/crates/bevy_reflect/src/func/args/arg.rs +++ b/crates/bevy_reflect/src/func/args/arg.rs @@ -196,8 +196,11 @@ impl<'a> Arg<'a> { /// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut #[derive(Debug)] pub enum ArgValue<'a> { + /// An owned argument. Owned(Box), + /// An immutable reference argument. Ref(&'a dyn PartialReflect), + /// A mutable reference argument. Mut(&'a mut dyn PartialReflect), } diff --git a/crates/bevy_reflect/src/func/args/error.rs b/crates/bevy_reflect/src/func/args/error.rs index bd32bd5e5a..20b6cd6220 100644 --- a/crates/bevy_reflect/src/func/args/error.rs +++ b/crates/bevy_reflect/src/func/args/error.rs @@ -12,15 +12,21 @@ pub enum ArgError { /// The argument is not the expected type. #[error("expected `{expected}` but received `{received}` (@ argument index {index})")] UnexpectedType { + /// Argument index. index: usize, + /// Expected argument type path. expected: Cow<'static, str>, + /// Received argument type path. received: Cow<'static, str>, }, /// The argument has the wrong ownership. #[error("expected {expected} value but received {received} value (@ argument index {index})")] InvalidOwnership { + /// Argument index. index: usize, + /// Expected ownership. expected: Ownership, + /// Received ownership. received: Ownership, }, /// Occurs when attempting to access an argument from an empty [`ArgList`]. diff --git a/crates/bevy_reflect/src/func/error.rs b/crates/bevy_reflect/src/func/error.rs index d9d105db1b..dc442e9da8 100644 --- a/crates/bevy_reflect/src/func/error.rs +++ b/crates/bevy_reflect/src/func/error.rs @@ -18,11 +18,18 @@ pub enum FunctionError { ArgError(#[from] ArgError), /// The number of arguments provided does not match the expected number. #[error("received {received} arguments but expected one of {expected:?}")] - ArgCountMismatch { expected: ArgCount, received: usize }, + ArgCountMismatch { + /// Expected argument count. [`ArgCount`] for overloaded functions will contain multiple possible counts. + expected: ArgCount, + /// Number of arguments received. + received: usize, + }, /// No overload was found for the given set of arguments. #[error("no overload found for arguments with signature `{received:?}`, expected one of `{expected:?}`")] NoOverload { + /// The set of available argument signatures. expected: HashSet, + /// The received argument signature. received: ArgumentSignature, }, } @@ -47,6 +54,9 @@ pub enum FunctionOverloadError { /// An error that occurs when attempting to add a function overload with a duplicate signature. #[error("could not add function overload: duplicate found for signature `{0:?}`")] DuplicateSignature(ArgumentSignature), + /// An attempt was made to add an overload with more than [`ArgCount::MAX_COUNT`] arguments. + /// + /// [`ArgCount::MAX_COUNT`]: crate::func::args::ArgCount #[error( "argument signature `{:?}` has too many arguments (max {})", 0, diff --git a/crates/bevy_reflect/src/func/info.rs b/crates/bevy_reflect/src/func/info.rs index 4b130e5772..2f5f82fbf5 100644 --- a/crates/bevy_reflect/src/func/info.rs +++ b/crates/bevy_reflect/src/func/info.rs @@ -235,6 +235,12 @@ impl TryFrom<[SignatureInfo; N]> for FunctionInfo { } } +/// Type information for the signature of a [`DynamicFunction`] or [`DynamicFunctionMut`]. +/// +/// Every [`FunctionInfo`] contains one or more [`SignatureInfo`]s. +/// +/// [`DynamicFunction`]: crate::func::DynamicFunction +/// [`DynamicFunctionMut`]: crate::func::DynamicFunctionMut #[derive(Debug, Clone)] pub struct SignatureInfo { name: Option>, diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs index 74a89282c6..237bc9eafc 100644 --- a/crates/bevy_reflect/src/func/mod.rs +++ b/crates/bevy_reflect/src/func/mod.rs @@ -42,7 +42,7 @@ //! //! A "function" is a callable that does not capture its environment. //! These are typically defined with the `fn` keyword, which are referred to as _named_ functions. -//! But they are also _anonymous_ functions, which are unnamed and defined with anonymous function syntax. +//! But there are also _anonymous_ functions, which are unnamed and defined with anonymous function syntax. //! //! ```rust //! // This is a named function: diff --git a/crates/bevy_reflect/src/func/registry.rs b/crates/bevy_reflect/src/func/registry.rs index e476353b8b..08ed7bd7f1 100644 --- a/crates/bevy_reflect/src/func/registry.rs +++ b/crates/bevy_reflect/src/func/registry.rs @@ -336,6 +336,7 @@ impl Debug for FunctionRegistry { /// A synchronized wrapper around a [`FunctionRegistry`]. #[derive(Clone, Default, Debug)] pub struct FunctionRegistryArc { + /// The wrapped [`FunctionRegistry`]. pub internal: Arc>, } diff --git a/crates/bevy_reflect/src/impls/core/primitives.rs b/crates/bevy_reflect/src/impls/core/primitives.rs index 8bee33b4c8..3600f2ece5 100644 --- a/crates/bevy_reflect/src/impls/core/primitives.rs +++ b/crates/bevy_reflect/src/impls/core/primitives.rs @@ -282,6 +282,7 @@ impl GetTypeRegistration for &'static str { let mut registration = TypeRegistration::of::(); registration.insert::(FromType::::from_type()); registration.insert::(FromType::::from_type()); + registration.insert::(FromType::::from_type()); registration } } diff --git a/crates/bevy_reflect/src/kind.rs b/crates/bevy_reflect/src/kind.rs index 3eef10d0e5..3764e863ba 100644 --- a/crates/bevy_reflect/src/kind.rs +++ b/crates/bevy_reflect/src/kind.rs @@ -134,7 +134,9 @@ macro_rules! impl_reflect_kind_conversions { #[derive(Debug, Error)] #[error("kind mismatch: expected {expected:?}, received {received:?}")] pub struct ReflectKindMismatchError { + /// Expected kind. pub expected: ReflectKind, + /// Received kind. pub received: ReflectKind, } @@ -176,16 +178,46 @@ macro_rules! impl_cast_method { /// /// ["kinds"]: ReflectKind pub enum ReflectRef<'a> { + /// An immutable reference to a [struct-like] type. + /// + /// [struct-like]: Struct Struct(&'a dyn Struct), + /// An immutable reference to a [tuple-struct-like] type. + /// + /// [tuple-struct-like]: TupleStruct TupleStruct(&'a dyn TupleStruct), + /// An immutable reference to a [tuple-like] type. + /// + /// [tuple-like]: Tuple Tuple(&'a dyn Tuple), + /// An immutable reference to a [list-like] type. + /// + /// [list-like]: List List(&'a dyn List), + /// An immutable reference to an [array-like] type. + /// + /// [array-like]: Array Array(&'a dyn Array), + /// An immutable reference to a [map-like] type. + /// + /// [map-like]: Map Map(&'a dyn Map), + /// An immutable reference to a [set-like] type. + /// + /// [set-like]: Set Set(&'a dyn Set), + /// An immutable reference to an [enum-like] type. + /// + /// [enum-like]: Enum Enum(&'a dyn Enum), + /// An immutable reference to a [function-like] type. + /// + /// [function-like]: Function #[cfg(feature = "functions")] Function(&'a dyn Function), + /// An immutable refeence to an [opaque] type. + /// + /// [opaque]: ReflectKind::Opaque Opaque(&'a dyn PartialReflect), } impl_reflect_kind_conversions!(ReflectRef<'_>); @@ -211,16 +243,46 @@ impl<'a> ReflectRef<'a> { /// /// ["kinds"]: ReflectKind pub enum ReflectMut<'a> { + /// A mutable reference to a [struct-like] type. + /// + /// [struct-like]: Struct Struct(&'a mut dyn Struct), + /// A mutable reference to a [tuple-struct-like] type. + /// + /// [tuple-struct-like]: TupleStruct TupleStruct(&'a mut dyn TupleStruct), + /// A mutable reference to a [tuple-like] type. + /// + /// [tuple-like]: Tuple Tuple(&'a mut dyn Tuple), + /// A mutable reference to a [list-like] type. + /// + /// [list-like]: List List(&'a mut dyn List), + /// A mutable reference to an [array-like] type. + /// + /// [array-like]: Array Array(&'a mut dyn Array), + /// A mutable reference to a [map-like] type. + /// + /// [map-like]: Map Map(&'a mut dyn Map), + /// A mutable reference to a [set-like] type. + /// + /// [set-like]: Set Set(&'a mut dyn Set), + /// A mutable reference to an [enum-like] type. + /// + /// [enum-like]: Enum Enum(&'a mut dyn Enum), #[cfg(feature = "functions")] + /// A mutable reference to a [function-like] type. + /// + /// [function-like]: Function Function(&'a mut dyn Function), + /// A mutable refeence to an [opaque] type. + /// + /// [opaque]: ReflectKind::Opaque Opaque(&'a mut dyn PartialReflect), } impl_reflect_kind_conversions!(ReflectMut<'_>); @@ -246,16 +308,46 @@ impl<'a> ReflectMut<'a> { /// /// ["kinds"]: ReflectKind pub enum ReflectOwned { + /// An owned [struct-like] type. + /// + /// [struct-like]: Struct Struct(Box), + /// An owned [tuple-struct-like] type. + /// + /// [tuple-struct-like]: TupleStruct TupleStruct(Box), + /// An owned [tuple-like] type. + /// + /// [tuple-like]: Tuple Tuple(Box), + /// An owned [list-like] type. + /// + /// [list-like]: List List(Box), + /// An owned [array-like] type. + /// + /// [array-like]: Array Array(Box), + /// An owned [map-like] type. + /// + /// [map-like]: Map Map(Box), + /// An owned [set-like] type. + /// + /// [set-like]: Set Set(Box), + /// An owned [enum-like] type. + /// + /// [enum-like]: Enum Enum(Box), + /// An owned [function-like] type. + /// + /// [function-like]: Function #[cfg(feature = "functions")] Function(Box), + /// An owned [opaque] type. + /// + /// [opaque]: ReflectKind::Opaque Opaque(Box), } impl_reflect_kind_conversions!(ReflectOwned); diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 0f399afd59..eabfdc0eac 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -1,4 +1,3 @@ -#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")] #![cfg_attr( any(docsrs, docsrs_dep), expect( diff --git a/crates/bevy_reflect/src/map.rs b/crates/bevy_reflect/src/map.rs index 1a1fcefb63..20531569e8 100644 --- a/crates/bevy_reflect/src/map.rs +++ b/crates/bevy_reflect/src/map.rs @@ -192,6 +192,8 @@ impl MapInfo { impl_generic_info_methods!(generics); } +/// Used to produce an error message when an attempt is made to hash +/// a [`PartialReflect`] value that does not support hashing. #[macro_export] macro_rules! hash_error { ( $key:expr ) => {{ diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index 04e4a2a4b0..c1e283a5f4 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -21,32 +21,47 @@ pub enum ApplyError { #[error("attempted to apply `{from_kind}` to `{to_kind}`")] /// Attempted to apply the wrong [kind](ReflectKind) to a type, e.g. a struct to an enum. MismatchedKinds { + /// Kind of the value we attempted to apply. from_kind: ReflectKind, + /// Kind of the type we attempted to apply the value to. to_kind: ReflectKind, }, #[error("enum variant `{variant_name}` doesn't have a field named `{field_name}`")] /// Enum variant that we tried to apply to was missing a field. MissingEnumField { + /// Name of the enum variant. variant_name: Box, + /// Name of the missing field. field_name: Box, }, #[error("`{from_type}` is not `{to_type}`")] /// Tried to apply incompatible types. MismatchedTypes { + /// Type of the value we attempted to apply. from_type: Box, + /// Type we attempted to apply the value to. to_type: Box, }, #[error("attempted to apply type with {from_size} size to a type with {to_size} size")] - /// Attempted to apply to types with mismatched sizes, e.g. a [u8; 4] to [u8; 3]. - DifferentSize { from_size: usize, to_size: usize }, + /// Attempted to apply an [array-like] type to another of different size, e.g. a [u8; 4] to [u8; 3]. + /// + /// [array-like]: crate::Array + DifferentSize { + /// Size of the value we attempted to apply, in elements. + from_size: usize, + /// Size of the type we attempted to apply the value to, in elements. + to_size: usize, + }, #[error("variant with name `{variant_name}` does not exist on enum `{enum_name}`")] /// The enum we tried to apply to didn't contain a variant with the give name. UnknownVariant { + /// Name of the enum. enum_name: Box, + /// Name of the missing variant. variant_name: Box, }, } diff --git a/crates/bevy_reflect/src/serde/de/deserialize_with_registry.rs b/crates/bevy_reflect/src/serde/de/deserialize_with_registry.rs index f92a8e68e2..8a216f87b9 100644 --- a/crates/bevy_reflect/src/serde/de/deserialize_with_registry.rs +++ b/crates/bevy_reflect/src/serde/de/deserialize_with_registry.rs @@ -42,6 +42,9 @@ use serde::Deserializer; /// [`ReflectDeserializer`]: crate::serde::ReflectDeserializer /// [via the registry]: TypeRegistry::register_type_data pub trait DeserializeWithRegistry<'de>: Sized { + /// Deserialize this value using the given [Deserializer] and [`TypeRegistry`]. + /// + /// [`Deserializer`]: ::serde::Deserializer fn deserialize(deserializer: D, registry: &TypeRegistry) -> Result where D: Deserializer<'de>; diff --git a/crates/bevy_reflect/src/serde/de/registrations.rs b/crates/bevy_reflect/src/serde/de/registrations.rs index adc0025c54..768b8ed32f 100644 --- a/crates/bevy_reflect/src/serde/de/registrations.rs +++ b/crates/bevy_reflect/src/serde/de/registrations.rs @@ -15,6 +15,7 @@ pub struct TypeRegistrationDeserializer<'a> { } impl<'a> TypeRegistrationDeserializer<'a> { + /// Creates a new [`TypeRegistrationDeserializer`]. pub fn new(registry: &'a TypeRegistry) -> Self { Self { registry } } diff --git a/crates/bevy_reflect/src/serde/mod.rs b/crates/bevy_reflect/src/serde/mod.rs index 032590e0c7..2ee47d4a7f 100644 --- a/crates/bevy_reflect/src/serde/mod.rs +++ b/crates/bevy_reflect/src/serde/mod.rs @@ -1,3 +1,5 @@ +//! Serde integration for reflected types. + mod de; mod ser; mod type_data; diff --git a/crates/bevy_reflect/src/serde/ser/serializable.rs b/crates/bevy_reflect/src/serde/ser/serializable.rs index 6a8a4c978f..c83737c842 100644 --- a/crates/bevy_reflect/src/serde/ser/serializable.rs +++ b/crates/bevy_reflect/src/serde/ser/serializable.rs @@ -3,7 +3,9 @@ use core::ops::Deref; /// A type-erased serializable value. pub enum Serializable<'a> { + /// An owned serializable value. Owned(Box), + /// An immutable reference to a serializable value. Borrowed(&'a dyn erased_serde::Serialize), } diff --git a/crates/bevy_reflect/src/serde/ser/serialize_with_registry.rs b/crates/bevy_reflect/src/serde/ser/serialize_with_registry.rs index 9c5bfb06f1..f9e6370799 100644 --- a/crates/bevy_reflect/src/serde/ser/serialize_with_registry.rs +++ b/crates/bevy_reflect/src/serde/ser/serialize_with_registry.rs @@ -40,6 +40,9 @@ use serde::{Serialize, Serializer}; /// [`ReflectSerializer`]: crate::serde::ReflectSerializer /// [via the registry]: TypeRegistry::register_type_data pub trait SerializeWithRegistry { + /// Serialize this value using the given [Serializer] and [`TypeRegistry`]. + /// + /// [`Serializer`]: ::serde::Serializer fn serialize(&self, serializer: S, registry: &TypeRegistry) -> Result where S: Serializer; diff --git a/crates/bevy_reflect/src/std_traits.rs b/crates/bevy_reflect/src/std_traits.rs index cad001132b..9b7f46c300 100644 --- a/crates/bevy_reflect/src/std_traits.rs +++ b/crates/bevy_reflect/src/std_traits.rs @@ -1,3 +1,5 @@ +//! Module containing the [`ReflectDefault`] type. + use crate::{FromType, Reflect}; use alloc::boxed::Box; @@ -10,6 +12,7 @@ pub struct ReflectDefault { } impl ReflectDefault { + /// Returns the default value for a type. pub fn default(&self) -> Box { (self.default)() } diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index 4346f55e27..e419947b3a 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -71,6 +71,7 @@ pub trait Struct: PartialReflect { /// Returns an iterator over the values of the reflectable fields for this struct. fn iter_fields(&self) -> FieldIter; + /// Creates a new [`DynamicStruct`] from this struct. fn to_dynamic_struct(&self) -> DynamicStruct { let mut dynamic_struct = DynamicStruct::default(); dynamic_struct.set_represented_type(self.get_represented_type_info()); @@ -192,6 +193,7 @@ pub struct FieldIter<'a> { } impl<'a> FieldIter<'a> { + /// Creates a new [`FieldIter`]. pub fn new(value: &'a dyn Struct) -> Self { FieldIter { struct_val: value, diff --git a/crates/bevy_reflect/src/tuple.rs b/crates/bevy_reflect/src/tuple.rs index 8bdd08099b..51f402c698 100644 --- a/crates/bevy_reflect/src/tuple.rs +++ b/crates/bevy_reflect/src/tuple.rs @@ -76,6 +76,7 @@ pub struct TupleFieldIter<'a> { } impl<'a> TupleFieldIter<'a> { + /// Creates a new [`TupleFieldIter`]. pub fn new(value: &'a dyn Tuple) -> Self { TupleFieldIter { tuple: value, diff --git a/crates/bevy_reflect/src/tuple_struct.rs b/crates/bevy_reflect/src/tuple_struct.rs index ab5b99a96b..cceab9904e 100644 --- a/crates/bevy_reflect/src/tuple_struct.rs +++ b/crates/bevy_reflect/src/tuple_struct.rs @@ -146,6 +146,7 @@ pub struct TupleStructFieldIter<'a> { } impl<'a> TupleStructFieldIter<'a> { + /// Creates a new [`TupleStructFieldIter`]. pub fn new(value: &'a dyn TupleStruct) -> Self { TupleStructFieldIter { tuple_struct: value, diff --git a/crates/bevy_reflect/src/type_info.rs b/crates/bevy_reflect/src/type_info.rs index 1a3be15c36..122ace0293 100644 --- a/crates/bevy_reflect/src/type_info.rs +++ b/crates/bevy_reflect/src/type_info.rs @@ -169,7 +169,9 @@ pub enum TypeInfoError { /// [kind]: ReflectKind #[error("kind mismatch: expected {expected:?}, received {received:?}")] KindMismatch { + /// Expected kind. expected: ReflectKind, + /// Received kind. received: ReflectKind, }, } @@ -183,7 +185,7 @@ pub enum TypeInfoError { /// 3. [`PartialReflect::get_represented_type_info`] /// 4. [`TypeRegistry::get_type_info`] /// -/// Each return a static reference to [`TypeInfo`], but they all have their own use cases. +/// Each returns a static reference to [`TypeInfo`], but they all have their own use cases. /// For example, if you know the type at compile time, [`Typed::type_info`] is probably /// the simplest. If you have a `dyn Reflect` you can use [`DynamicTyped::reflect_type_info`]. /// If all you have is a `dyn PartialReflect`, you'll probably want [`PartialReflect::get_represented_type_info`]. @@ -199,14 +201,40 @@ pub enum TypeInfoError { /// [type path]: TypePath::type_path #[derive(Debug, Clone)] pub enum TypeInfo { + /// Type information for a [struct-like] type. + /// + /// [struct-like]: crate::Struct Struct(StructInfo), + /// Type information for a [tuple-struct-like] type. + /// + /// [tuple-struct-like]: crate::TupleStruct TupleStruct(TupleStructInfo), + /// Type information for a [tuple-like] type. + /// + /// [tuple-like]: crate::Tuple Tuple(TupleInfo), + /// Type information for a [list-like] type. + /// + /// [list-like]: crate::List List(ListInfo), + /// Type information for an [array-like] type. + /// + /// [array-like]: crate::Array Array(ArrayInfo), + /// Type information for a [map-like] type. + /// + /// [map-like]: crate::Map Map(MapInfo), + /// Type information for a [set-like] type. + /// + /// [set-like]: crate::Set Set(SetInfo), + /// Type information for an [enum-like] type. + /// + /// [enum-like]: crate::Enum Enum(EnumInfo), + /// Type information for an opaque type - see the [`OpaqueInfo`] docs for + /// a discussion of opaque types. Opaque(OpaqueInfo), } @@ -557,6 +585,7 @@ pub struct OpaqueInfo { } impl OpaqueInfo { + /// Creates a new [`OpaqueInfo`]. pub fn new() -> Self { Self { ty: Type::of::(), diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 5827ebdac5..1f18396be2 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -38,6 +38,7 @@ pub struct TypeRegistry { /// A synchronized wrapper around a [`TypeRegistry`]. #[derive(Clone, Default)] pub struct TypeRegistryArc { + /// The wrapped [`TypeRegistry`]. pub internal: Arc>, } @@ -313,6 +314,7 @@ impl TypeRegistry { data.insert(D::from_type()); } + /// Whether the type with given [`TypeId`] has been registered in this registry. pub fn contains(&self, type_id: TypeId) -> bool { self.registrations.contains_key(&type_id) } @@ -684,6 +686,7 @@ impl Clone for TypeRegistration { /// /// [crate-level documentation]: crate pub trait TypeData: Downcast + Send + Sync { + /// Creates a type-erased clone of this value. fn clone_type_data(&self) -> Box; } impl_downcast!(TypeData); @@ -702,6 +705,7 @@ where /// This is used by the `#[derive(Reflect)]` macro to generate an implementation /// of [`TypeData`] to pass to [`TypeRegistration::insert`]. pub trait FromType { + /// Creates an instance of `Self` for type `T`. fn from_type() -> Self; } @@ -746,6 +750,8 @@ impl ReflectSerialize { /// [`FromType::from_type`]. #[derive(Clone)] pub struct ReflectDeserialize { + /// Function used by [`ReflectDeserialize::deserialize`] to + /// perform deserialization. pub func: fn( deserializer: &mut dyn erased_serde::Deserializer, ) -> Result, erased_serde::Error>, diff --git a/crates/bevy_reflect/src/utility.rs b/crates/bevy_reflect/src/utility.rs index 5735a29dbe..db8416bd6c 100644 --- a/crates/bevy_reflect/src/utility.rs +++ b/crates/bevy_reflect/src/utility.rs @@ -16,6 +16,7 @@ use core::{ /// /// [`Non`]: NonGenericTypeCell pub trait TypedProperty: sealed::Sealed { + /// The type of the value stored in [`GenericTypeCell`]. type Stored: 'static; } @@ -201,7 +202,7 @@ impl Default for NonGenericTypeCell { /// static CELL: GenericTypePathCell = GenericTypePathCell::new(); /// CELL.get_or_insert::(|| format!("my_crate::foo::Foo<{}>", T::type_path())) /// } -/// +/// /// fn short_type_path() -> &'static str { /// static CELL: GenericTypePathCell = GenericTypePathCell::new(); /// CELL.get_or_insert::(|| format!("Foo<{}>", T::short_type_path())) diff --git a/crates/bevy_remote/Cargo.toml b/crates/bevy_remote/Cargo.toml index ca84c2916e..176e5ed47f 100644 --- a/crates/bevy_remote/Cargo.toml +++ b/crates/bevy_remote/Cargo.toml @@ -21,7 +21,9 @@ bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", features = [ ] } bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev" } bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev" } -bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" } +bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", features = [ + "debug", +] } bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false, features = [ "std", "serialize", diff --git a/crates/bevy_remote/src/builtin_methods.rs b/crates/bevy_remote/src/builtin_methods.rs index 28bb506bc2..b6747217b1 100644 --- a/crates/bevy_remote/src/builtin_methods.rs +++ b/crates/bevy_remote/src/builtin_methods.rs @@ -1134,7 +1134,7 @@ pub fn process_remote_list_request(In(params): In>, world: &World) let Some(component_info) = world.components().get_info(component_id) else { continue; }; - response.push(component_info.name().to_owned()); + response.push(component_info.name().to_string()); } } // If `None`, list all registered components. @@ -1197,7 +1197,7 @@ pub fn process_remote_list_watching_request( let Some(component_info) = world.components().get_info(component_id) else { continue; }; - response.added.push(component_info.name().to_owned()); + response.added.push(component_info.name().to_string()); } } @@ -1210,7 +1210,7 @@ pub fn process_remote_list_watching_request( let Some(component_info) = world.components().get_info(*component_id) else { continue; }; - response.removed.push(component_info.name().to_owned()); + response.removed.push(component_info.name().to_string()); } } } diff --git a/crates/bevy_render/macros/src/extract_component.rs b/crates/bevy_render/macros/src/extract_component.rs index 2bfd0e0e11..8526f7b889 100644 --- a/crates/bevy_render/macros/src/extract_component.rs +++ b/crates/bevy_render/macros/src/extract_component.rs @@ -43,7 +43,7 @@ pub fn derive_extract_component(input: TokenStream) -> TokenStream { type QueryFilter = #filter; type Out = Self; - fn extract_component(item: #bevy_ecs_path::query::QueryItem<'_, Self::QueryData>) -> Option { + fn extract_component(item: #bevy_ecs_path::query::QueryItem<'_, '_, Self::QueryData>) -> Option { Some(item.clone()) } } diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index fd3b8cb4b2..2bddcd0d05 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -601,8 +601,7 @@ impl Camera { rect_relative.y = 1.0 - rect_relative.y; let ndc = rect_relative * 2. - Vec2::ONE; - let ndc_to_world = - camera_transform.compute_matrix() * self.computed.clip_from_view.inverse(); + let ndc_to_world = camera_transform.to_matrix() * self.computed.clip_from_view.inverse(); let world_near_plane = ndc_to_world.project_point3(ndc.extend(1.)); // Using EPSILON because an ndc with Z = 0 returns NaNs. let world_far_plane = ndc_to_world.project_point3(ndc.extend(f32::EPSILON)); @@ -668,7 +667,7 @@ impl Camera { ) -> Option { // Build a transformation matrix to convert from world space to NDC using camera data let clip_from_world: Mat4 = - self.computed.clip_from_view * camera_transform.compute_matrix().inverse(); + self.computed.clip_from_view * camera_transform.to_matrix().inverse(); let ndc_space_coords: Vec3 = clip_from_world.project_point3(world_position); (!ndc_space_coords.is_nan()).then_some(ndc_space_coords) @@ -689,8 +688,7 @@ impl Camera { /// Will panic if the projection matrix is invalid (has a determinant of 0) and `glam_assert` is enabled. pub fn ndc_to_world(&self, camera_transform: &GlobalTransform, ndc: Vec3) -> Option { // Build a transformation matrix to convert from NDC to world space using camera data - let ndc_to_world = - camera_transform.compute_matrix() * self.computed.clip_from_view.inverse(); + let ndc_to_world = camera_transform.to_matrix() * self.computed.clip_from_view.inverse(); let world_space_coords = ndc_to_world.project_point3(ndc); diff --git a/crates/bevy_render/src/camera/projection.rs b/crates/bevy_render/src/camera/projection.rs index a7796a1d1a..ee2a5080d2 100644 --- a/crates/bevy_render/src/camera/projection.rs +++ b/crates/bevy_render/src/camera/projection.rs @@ -93,8 +93,7 @@ pub trait CameraProjection { /// This code is called by [`update_frusta`](crate::view::visibility::update_frusta) system /// for each camera to update its frustum. fn compute_frustum(&self, camera_transform: &GlobalTransform) -> Frustum { - let clip_from_world = - self.get_clip_from_view() * camera_transform.compute_matrix().inverse(); + let clip_from_world = self.get_clip_from_view() * camera_transform.to_matrix().inverse(); Frustum::from_clip_from_world_custom_far( &clip_from_world, &camera_transform.translation(), diff --git a/crates/bevy_render/src/extract_component.rs b/crates/bevy_render/src/extract_component.rs index e1f528d6ab..b7bb05e425 100644 --- a/crates/bevy_render/src/extract_component.rs +++ b/crates/bevy_render/src/extract_component.rs @@ -60,7 +60,7 @@ pub trait ExtractComponent: Component { // type Out: Component = Self; /// Defines how the component is transferred into the "render world". - fn extract_component(item: QueryItem<'_, Self::QueryData>) -> Option; + fn extract_component(item: QueryItem<'_, '_, Self::QueryData>) -> Option; } /// This plugin prepares the components of the corresponding type for the GPU diff --git a/crates/bevy_render/src/extract_instances.rs b/crates/bevy_render/src/extract_instances.rs index a8e5a9ecbd..cd194b7c40 100644 --- a/crates/bevy_render/src/extract_instances.rs +++ b/crates/bevy_render/src/extract_instances.rs @@ -34,7 +34,7 @@ pub trait ExtractInstance: Send + Sync + Sized + 'static { type QueryFilter: QueryFilter; /// Defines how the component is transferred into the "render world". - fn extract(item: QueryItem<'_, Self::QueryData>) -> Option; + fn extract(item: QueryItem<'_, '_, Self::QueryData>) -> Option; } /// This plugin extracts one or more components into the "render world" as diff --git a/crates/bevy_render/src/render_graph/node.rs b/crates/bevy_render/src/render_graph/node.rs index 0a634c2598..4355892487 100644 --- a/crates/bevy_render/src/render_graph/node.rs +++ b/crates/bevy_render/src/render_graph/node.rs @@ -366,7 +366,7 @@ pub trait ViewNode { &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext<'w>, - view_query: QueryItem<'w, Self::ViewQuery>, + view_query: QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError>; } diff --git a/crates/bevy_render/src/render_phase/draw.rs b/crates/bevy_render/src/render_phase/draw.rs index 374dc6b330..42a6725c8c 100644 --- a/crates/bevy_render/src/render_phase/draw.rs +++ b/crates/bevy_render/src/render_phase/draw.rs @@ -213,8 +213,8 @@ pub trait RenderCommand { /// issuing draw calls, etc.) via the [`TrackedRenderPass`]. fn render<'w>( item: &P, - view: ROQueryItem<'w, Self::ViewQuery>, - entity: Option>, + view: ROQueryItem<'w, '_, Self::ViewQuery>, + entity: Option>, param: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult; @@ -246,8 +246,8 @@ macro_rules! render_command_tuple_impl { )] fn render<'w>( _item: &P, - ($($view,)*): ROQueryItem<'w, Self::ViewQuery>, - maybe_entities: Option>, + ($($view,)*): ROQueryItem<'w, '_, Self::ViewQuery>, + maybe_entities: Option>, ($($name,)*): SystemParamItem<'w, '_, Self::Param>, _pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { diff --git a/crates/bevy_render/src/sync_world.rs b/crates/bevy_render/src/sync_world.rs index 6dceaba3c0..b216b5fd32 100644 --- a/crates/bevy_render/src/sync_world.rs +++ b/crates/bevy_render/src/sync_world.rs @@ -281,7 +281,7 @@ mod render_entities_world_query_impls { archetype::Archetype, component::{ComponentId, Components, Tick}, entity::Entity, - query::{FilteredAccess, QueryData, ReadOnlyQueryData, WorldQuery}, + query::{FilteredAccess, QueryData, ReadOnlyQueryData, ReleaseStateQueryData, WorldQuery}, storage::{Table, TableRow}, world::{unsafe_world_cell::UnsafeWorldCell, World}, }; @@ -299,9 +299,9 @@ mod render_entities_world_query_impls { } #[inline] - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - component_id: &ComponentId, + component_id: &'s ComponentId, last_run: Tick, this_run: Tick, ) -> Self::Fetch<'w> { @@ -314,9 +314,9 @@ mod render_entities_world_query_impls { const IS_DENSE: bool = <&'static RenderEntity as WorldQuery>::IS_DENSE; #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( fetch: &mut Self::Fetch<'w>, - component_id: &ComponentId, + component_id: &'s ComponentId, archetype: &'w Archetype, table: &'w Table, ) { @@ -327,9 +327,9 @@ mod render_entities_world_query_impls { } #[inline] - unsafe fn set_table<'w>( + unsafe fn set_table<'w, 's>( fetch: &mut Self::Fetch<'w>, - &component_id: &ComponentId, + &component_id: &'s ComponentId, table: &'w Table, ) { // SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`. @@ -364,21 +364,24 @@ mod render_entities_world_query_impls { unsafe impl QueryData for RenderEntity { const IS_READ_ONLY: bool = true; type ReadOnly = RenderEntity; - type Item<'w> = Entity; + type Item<'w, 's> = Entity; - fn shrink<'wlong: 'wshort, 'wshort>(item: Entity) -> Entity { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { // SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`. let component = - unsafe { <&RenderEntity as QueryData>::fetch(fetch, entity, table_row) }; + unsafe { <&RenderEntity as QueryData>::fetch(state, fetch, entity, table_row) }; component.id() } } @@ -386,6 +389,12 @@ mod render_entities_world_query_impls { // SAFETY: the underlying `Entity` is copied, and no mutable access is provided. unsafe impl ReadOnlyQueryData for RenderEntity {} + impl ReleaseStateQueryData for RenderEntity { + fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { + item + } + } + /// SAFETY: defers completely to `&RenderEntity` implementation, /// and then only modifies the output safely. unsafe impl WorldQuery for MainEntity { @@ -399,9 +408,9 @@ mod render_entities_world_query_impls { } #[inline] - unsafe fn init_fetch<'w>( + unsafe fn init_fetch<'w, 's>( world: UnsafeWorldCell<'w>, - component_id: &ComponentId, + component_id: &'s ComponentId, last_run: Tick, this_run: Tick, ) -> Self::Fetch<'w> { @@ -414,7 +423,7 @@ mod render_entities_world_query_impls { const IS_DENSE: bool = <&'static MainEntity as WorldQuery>::IS_DENSE; #[inline] - unsafe fn set_archetype<'w>( + unsafe fn set_archetype<'w, 's>( fetch: &mut Self::Fetch<'w>, component_id: &ComponentId, archetype: &'w Archetype, @@ -427,9 +436,9 @@ mod render_entities_world_query_impls { } #[inline] - unsafe fn set_table<'w>( + unsafe fn set_table<'w, 's>( fetch: &mut Self::Fetch<'w>, - &component_id: &ComponentId, + &component_id: &'s ComponentId, table: &'w Table, ) { // SAFETY: defers to the `&T` implementation, with T set to `MainEntity`. @@ -464,26 +473,36 @@ mod render_entities_world_query_impls { unsafe impl QueryData for MainEntity { const IS_READ_ONLY: bool = true; type ReadOnly = MainEntity; - type Item<'w> = Entity; + type Item<'w, 's> = Entity; - fn shrink<'wlong: 'wshort, 'wshort>(item: Entity) -> Entity { + fn shrink<'wlong: 'wshort, 'wshort, 's>( + item: Self::Item<'wlong, 's>, + ) -> Self::Item<'wshort, 's> { item } #[inline(always)] - unsafe fn fetch<'w>( + unsafe fn fetch<'w, 's>( + state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, - ) -> Self::Item<'w> { + ) -> Self::Item<'w, 's> { // SAFETY: defers to the `&T` implementation, with T set to `MainEntity`. - let component = unsafe { <&MainEntity as QueryData>::fetch(fetch, entity, table_row) }; + let component = + unsafe { <&MainEntity as QueryData>::fetch(state, fetch, entity, table_row) }; component.id() } } // SAFETY: the underlying `Entity` is copied, and no mutable access is provided. unsafe impl ReadOnlyQueryData for MainEntity {} + + impl ReleaseStateQueryData for MainEntity { + fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { + item + } + } } #[cfg(test)] diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index d25e8e7f49..b2b90d0b24 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -316,7 +316,7 @@ pub struct ExtractedView { impl ExtractedView { /// Creates a 3D rangefinder for a view pub fn rangefinder3d(&self) -> ViewRangefinder3d { - ViewRangefinder3d::from_world_from_view(&self.world_from_view.compute_matrix()) + ViewRangefinder3d::from_world_from_view(&self.world_from_view.to_matrix()) } } @@ -934,7 +934,7 @@ pub fn prepare_view_uniforms( } let view_from_clip = clip_from_view.inverse(); - let world_from_view = extracted_view.world_from_view.compute_matrix(); + let world_from_view = extracted_view.world_from_view.to_matrix(); let view_from_world = world_from_view.inverse(); let clip_from_world = if temporal_jitter.is_some() { diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 1d684c9dac..2293beef1e 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -93,7 +93,7 @@ impl Scene { type_registry .get(type_id) .ok_or_else(|| SceneSpawnError::UnregisteredType { - std_type_name: component_info.name().to_string(), + std_type_name: component_info.name(), })?; let reflect_resource = registration.data::().ok_or_else(|| { SceneSpawnError::UnregisteredResource { @@ -133,7 +133,7 @@ impl Scene { let registration = type_registry .get(component_info.type_id().unwrap()) .ok_or_else(|| SceneSpawnError::UnregisteredType { - std_type_name: component_info.name().to_string(), + std_type_name: component_info.name(), })?; let reflect_component = registration.data::().ok_or_else(|| { diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index a15d00f116..71cd848751 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -10,6 +10,7 @@ use bevy_ecs::{ }; use bevy_platform::collections::{HashMap, HashSet}; use bevy_reflect::Reflect; +use bevy_utils::prelude::DebugName; use thiserror::Error; use uuid::Uuid; @@ -105,7 +106,7 @@ pub enum SceneSpawnError { )] UnregisteredType { /// The [type name](std::any::type_name) for the unregistered type. - std_type_name: String, + std_type_name: DebugName, }, /// Scene contains an unregistered type which has a `TypePath`. #[error( diff --git a/crates/bevy_solari/src/scene/binder.rs b/crates/bevy_solari/src/scene/binder.rs index 59a8020566..889efb538e 100644 --- a/crates/bevy_solari/src/scene/binder.rs +++ b/crates/bevy_solari/src/scene/binder.rs @@ -143,7 +143,7 @@ pub fn prepare_raytracing_scene_bindings( continue; }; - let transform = transform.compute_matrix(); + let transform = transform.to_matrix(); *tlas.get_mut_single(instance_id).unwrap() = Some(TlasInstance::new( blas, tlas_transform(&transform), diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index a3d9ee3eb2..204dd85e24 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -778,7 +778,7 @@ impl RenderCommand

for SetMesh2dViewBindGroup( _item: &P, - (view_uniform, mesh2d_view_bind_group): ROQueryItem<'w, Self::ViewQuery>, + (view_uniform, mesh2d_view_bind_group): ROQueryItem<'w, '_, Self::ViewQuery>, _view: Option<()>, _param: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, diff --git a/crates/bevy_sprite/src/mesh2d/wireframe2d.rs b/crates/bevy_sprite/src/mesh2d/wireframe2d.rs index 8ffb12a582..03de94be1c 100644 --- a/crates/bevy_sprite/src/mesh2d/wireframe2d.rs +++ b/crates/bevy_sprite/src/mesh2d/wireframe2d.rs @@ -372,7 +372,7 @@ impl ViewNode for Wireframe2dNode { &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext<'w>, - (camera, view, target, depth): QueryItem<'w, Self::ViewQuery>, + (camera, view, target, depth): QueryItem<'w, '_, Self::ViewQuery>, world: &'w World, ) -> Result<(), NodeRunError> { let Some(wireframe_phase) = diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 7602addc0b..761e2c628a 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -908,7 +908,7 @@ impl RenderCommand

for SetSpriteViewBindGroup( _item: &P, - (view_uniform, sprite_view_bind_group): ROQueryItem<'w, Self::ViewQuery>, + (view_uniform, sprite_view_bind_group): ROQueryItem<'w, '_, Self::ViewQuery>, _entity: Option<()>, _param: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, @@ -925,7 +925,7 @@ impl RenderCommand

for SetSpriteTextureBindGrou fn render<'w>( item: &P, - view: ROQueryItem<'w, Self::ViewQuery>, + view: ROQueryItem<'w, '_, Self::ViewQuery>, _entity: Option<()>, (image_bind_groups, batches): SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, @@ -955,7 +955,7 @@ impl RenderCommand

for DrawSpriteBatch { fn render<'w>( item: &P, - view: ROQueryItem<'w, Self::ViewQuery>, + view: ROQueryItem<'w, '_, Self::ViewQuery>, _entity: Option<()>, (sprite_meta, batches): SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, diff --git a/crates/bevy_state/src/state/mod.rs b/crates/bevy_state/src/state/mod.rs index 9267478281..61ee0627a3 100644 --- a/crates/bevy_state/src/state/mod.rs +++ b/crates/bevy_state/src/state/mod.rs @@ -642,6 +642,203 @@ mod tests { } } + #[derive(PartialEq, Eq, Debug, Hash, Clone)] + enum MultiSourceComputedState { + FromSimpleBTrue, + FromSimple2B2, + FromBoth, + } + + impl ComputedStates for MultiSourceComputedState { + type SourceStates = (SimpleState, SimpleState2); + + fn compute((simple_state, simple_state2): (SimpleState, SimpleState2)) -> Option { + match (simple_state, simple_state2) { + // If both are in their special states, prioritize the "both" variant. + (SimpleState::B(true), SimpleState2::B2) => Some(Self::FromBoth), + // If only SimpleState is B(true). + (SimpleState::B(true), _) => Some(Self::FromSimpleBTrue), + // If only SimpleState2 is B2. + (_, SimpleState2::B2) => Some(Self::FromSimple2B2), + // Otherwise, no computed state. + _ => None, + } + } + } + + /// This test ensures that [`ComputedStates`] with multiple source states + /// react when any source changes. + #[test] + fn computed_state_with_multiple_sources_should_react_to_any_source_change() { + let mut world = World::new(); + EventRegistry::register_event::>(&mut world); + EventRegistry::register_event::>(&mut world); + EventRegistry::register_event::>(&mut world); + + world.init_resource::>(); + world.init_resource::>(); + + let mut schedules = Schedules::new(); + let mut apply_changes = Schedule::new(StateTransition); + SimpleState::register_state(&mut apply_changes); + SimpleState2::register_state(&mut apply_changes); + MultiSourceComputedState::register_computed_state_systems(&mut apply_changes); + schedules.insert(apply_changes); + + world.insert_resource(schedules); + setup_state_transitions_in_world(&mut world); + + // Initial state: SimpleState::A, SimpleState2::A1 and + // MultiSourceComputedState should not exist yet. + world.run_schedule(StateTransition); + assert_eq!(world.resource::>().0, SimpleState::A); + assert_eq!(world.resource::>().0, SimpleState2::A1); + assert!(!world.contains_resource::>()); + + // Change only SimpleState to B(true) - this should trigger + // MultiSourceComputedState. + world.insert_resource(NextState::Pending(SimpleState::B(true))); + world.run_schedule(StateTransition); + assert_eq!( + world.resource::>().0, + SimpleState::B(true) + ); + assert_eq!(world.resource::>().0, SimpleState2::A1); + // The computed state should exist because SimpleState changed to + // B(true). + assert!(world.contains_resource::>()); + assert_eq!( + world.resource::>().0, + MultiSourceComputedState::FromSimpleBTrue + ); + + // Reset SimpleState to A - computed state should be removed. + world.insert_resource(NextState::Pending(SimpleState::A)); + world.run_schedule(StateTransition); + assert!(!world.contains_resource::>()); + + // Now change only SimpleState2 to B2 - this should also trigger + // MultiSourceComputedState. + world.insert_resource(NextState::Pending(SimpleState2::B2)); + world.run_schedule(StateTransition); + assert_eq!(world.resource::>().0, SimpleState::A); + assert_eq!(world.resource::>().0, SimpleState2::B2); + // The computed state should exist because SimpleState2 changed to B2. + assert!(world.contains_resource::>()); + assert_eq!( + world.resource::>().0, + MultiSourceComputedState::FromSimple2B2 + ); + + // Test that changes to both states work. + world.insert_resource(NextState::Pending(SimpleState::B(true))); + world.insert_resource(NextState::Pending(SimpleState2::A1)); + world.run_schedule(StateTransition); + assert_eq!( + world.resource::>().0, + MultiSourceComputedState::FromSimpleBTrue + ); + } + + // Test SubState that depends on multiple source states. + #[derive(PartialEq, Eq, Debug, Default, Hash, Clone)] + enum MultiSourceSubState { + #[default] + Active, + } + + impl SubStates for MultiSourceSubState { + type SourceStates = (SimpleState, SimpleState2); + + fn should_exist( + (simple_state, simple_state2): (SimpleState, SimpleState2), + ) -> Option { + // SubState should exist when: + // - SimpleState is B(true), OR + // - SimpleState2 is B2 + match (simple_state, simple_state2) { + (SimpleState::B(true), _) | (_, SimpleState2::B2) => Some(Self::Active), + _ => None, + } + } + } + + impl States for MultiSourceSubState { + const DEPENDENCY_DEPTH: usize = ::SourceStates::SET_DEPENDENCY_DEPTH + 1; + } + + impl FreelyMutableState for MultiSourceSubState {} + + /// This test ensures that [`SubStates`] with multiple source states react + /// when any source changes. + #[test] + fn sub_state_with_multiple_sources_should_react_to_any_source_change() { + let mut world = World::new(); + EventRegistry::register_event::>(&mut world); + EventRegistry::register_event::>(&mut world); + EventRegistry::register_event::>(&mut world); + + world.init_resource::>(); + world.init_resource::>(); + + let mut schedules = Schedules::new(); + let mut apply_changes = Schedule::new(StateTransition); + SimpleState::register_state(&mut apply_changes); + SimpleState2::register_state(&mut apply_changes); + MultiSourceSubState::register_sub_state_systems(&mut apply_changes); + schedules.insert(apply_changes); + + world.insert_resource(schedules); + setup_state_transitions_in_world(&mut world); + + // Initial state: SimpleState::A, SimpleState2::A1 and + // MultiSourceSubState should not exist yet. + world.run_schedule(StateTransition); + assert_eq!(world.resource::>().0, SimpleState::A); + assert_eq!(world.resource::>().0, SimpleState2::A1); + assert!(!world.contains_resource::>()); + + // Change only SimpleState to B(true) - this should trigger + // MultiSourceSubState. + world.insert_resource(NextState::Pending(SimpleState::B(true))); + world.run_schedule(StateTransition); + assert_eq!( + world.resource::>().0, + SimpleState::B(true) + ); + assert_eq!(world.resource::>().0, SimpleState2::A1); + // The sub state should exist because SimpleState changed to B(true). + assert!(world.contains_resource::>()); + + // Reset to initial state. + world.insert_resource(NextState::Pending(SimpleState::A)); + world.run_schedule(StateTransition); + assert!(!world.contains_resource::>()); + + // Now change only SimpleState2 to B2 - this should also trigger + // MultiSourceSubState creation. + world.insert_resource(NextState::Pending(SimpleState2::B2)); + world.run_schedule(StateTransition); + assert_eq!(world.resource::>().0, SimpleState::A); + assert_eq!(world.resource::>().0, SimpleState2::B2); + // The sub state should exist because SimpleState2 changed to B2. + assert!(world.contains_resource::>()); + + // Finally, test that it works when both change simultaneously. + world.insert_resource(NextState::Pending(SimpleState::B(false))); + world.insert_resource(NextState::Pending(SimpleState2::A1)); + world.run_schedule(StateTransition); + // After this transition, the state should not exist since SimpleState + // is B(false). + assert!(!world.contains_resource::>()); + + // Change both at the same time. + world.insert_resource(NextState::Pending(SimpleState::B(true))); + world.insert_resource(NextState::Pending(SimpleState2::B2)); + world.run_schedule(StateTransition); + assert!(world.contains_resource::>()); + } + #[test] fn check_transition_orders() { let mut world = World::new(); diff --git a/crates/bevy_state/src/state/state_set.rs b/crates/bevy_state/src/state/state_set.rs index 69a6c41b3d..3cf1e1d260 100644 --- a/crates/bevy_state/src/state/state_set.rs +++ b/crates/bevy_state/src/state/state_set.rs @@ -293,7 +293,7 @@ macro_rules! impl_state_set_sealed_tuples { current_state_res: Option>>, next_state_res: Option>>, ($($val),*,): ($(Option>>),*,)| { - let parent_changed = ($($evt.read().last().is_some())&&*); + let parent_changed = ($($evt.read().last().is_some())||*); let next_state = take_next_state(next_state_res); if !parent_changed && next_state.is_none() { diff --git a/crates/bevy_transform/src/components/global_transform.rs b/crates/bevy_transform/src/components/global_transform.rs index b10d5a9d1a..cd7db6ef71 100644 --- a/crates/bevy_transform/src/components/global_transform.rs +++ b/crates/bevy_transform/src/components/global_transform.rs @@ -115,7 +115,7 @@ impl GlobalTransform { /// Returns the 3d affine transformation matrix as a [`Mat4`]. #[inline] - pub fn compute_matrix(&self) -> Mat4 { + pub fn to_matrix(&self) -> Mat4 { Mat4::from(self.0) } @@ -139,8 +139,9 @@ impl GlobalTransform { } } - /// Returns the isometric part of the transformation as an [isometry]. Any scaling done by the - /// transformation will be ignored. + /// Computes a Scale-Rotation-Translation decomposition of the transformation and returns + /// the isometric part as an [isometry]. Any scaling done by the transformation will be ignored. + /// Note: this is a somewhat costly and lossy conversion. /// /// The transform is expected to be non-degenerate and without shearing, or the output /// will be invalid. diff --git a/crates/bevy_transform/src/components/transform.rs b/crates/bevy_transform/src/components/transform.rs index 7873ae743c..bc161e9e8c 100644 --- a/crates/bevy_transform/src/components/transform.rs +++ b/crates/bevy_transform/src/components/transform.rs @@ -256,10 +256,10 @@ impl Transform { self } - /// Returns the 3d affine transformation matrix from this transforms translation, + /// Computes the 3d affine transformation matrix from this transform's translation, /// rotation, and scale. #[inline] - pub fn compute_matrix(&self) -> Mat4 { + pub fn to_matrix(&self) -> Mat4 { Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation) } diff --git a/crates/bevy_ui/src/render/ui_material_pipeline.rs b/crates/bevy_ui/src/render/ui_material_pipeline.rs index ebdeacccf9..3ad4f4ea6a 100644 --- a/crates/bevy_ui/src/render/ui_material_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_material_pipeline.rs @@ -276,7 +276,7 @@ impl RenderCommand

fn render<'w>( _item: &P, _view: (), - material_handle: Option>, + material_handle: Option>, materials: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { diff --git a/crates/bevy_ui/src/ui_node.rs b/crates/bevy_ui/src/ui_node.rs index 1c5ed364b9..d81f80f8b9 100644 --- a/crates/bevy_ui/src/ui_node.rs +++ b/crates/bevy_ui/src/ui_node.rs @@ -2875,6 +2875,8 @@ impl ComputedNodeTarget { } /// Adds a shadow behind text +/// +/// Not supported by `Text2d` #[derive(Component, Copy, Clone, Debug, PartialEq, Reflect)] #[reflect(Component, Default, Debug, Clone, PartialEq)] pub struct TextShadow { diff --git a/crates/bevy_utils/Cargo.toml b/crates/bevy_utils/Cargo.toml index 53eda0d358..aaf6a0834e 100644 --- a/crates/bevy_utils/Cargo.toml +++ b/crates/bevy_utils/Cargo.toml @@ -16,9 +16,14 @@ wgpu_wrapper = ["dep:send_wrapper"] # Provides access to the `Parallel` type. parallel = ["bevy_platform/std", "dep:thread_local"] +std = ["disqualified/alloc"] + +debug = [] + [dependencies] bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false } +disqualified = { version = "1.0", default-features = false } thread_local = { version = "1.0", optional = true } [target.'cfg(all(target_arch = "wasm32", target_feature = "atomics"))'.dependencies] diff --git a/crates/bevy_utils/src/debug_info.rs b/crates/bevy_utils/src/debug_info.rs new file mode 100644 index 0000000000..c79c5ebe60 --- /dev/null +++ b/crates/bevy_utils/src/debug_info.rs @@ -0,0 +1,102 @@ +use alloc::{borrow::Cow, fmt, string::String}; +#[cfg(feature = "debug")] +use core::any::type_name; +use disqualified::ShortName; + +#[cfg(not(feature = "debug"))] +const FEATURE_DISABLED: &'static str = "Enable the debug feature to see the name"; + +/// Wrapper to help debugging ECS issues. This is used to display the names of systems, components, ... +/// +/// * If the `debug` feature is enabled, the actual name will be used +/// * If it is disabled, a string mentioning the disabled feature will be used +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DebugName { + #[cfg(feature = "debug")] + name: Cow<'static, str>, +} + +impl fmt::Display for DebugName { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "debug")] + f.write_str(self.name.as_ref())?; + #[cfg(not(feature = "debug"))] + f.write_str(FEATURE_DISABLED)?; + + Ok(()) + } +} + +impl DebugName { + /// Create a new `DebugName` from a `&str` + /// + /// The value will be ignored if the `debug` feature is not enabled + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] + pub fn borrowed(value: &'static str) -> Self { + DebugName { + #[cfg(feature = "debug")] + name: Cow::Borrowed(value), + } + } + + /// Create a new `DebugName` from a `String` + /// + /// The value will be ignored if the `debug` feature is not enabled + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] + pub fn owned(value: String) -> Self { + DebugName { + #[cfg(feature = "debug")] + name: Cow::Owned(value), + } + } + + /// Create a new `DebugName` from a type by using its [`core::any::type_name`] + /// + /// The value will be ignored if the `debug` feature is not enabled + pub fn type_name() -> Self { + DebugName { + #[cfg(feature = "debug")] + name: Cow::Borrowed(type_name::()), + } + } + + /// Get the [`ShortName`] corresping to this debug name + /// + /// The value will be a static string if the `debug` feature is not enabled + pub fn shortname(&self) -> ShortName { + #[cfg(feature = "debug")] + return ShortName(self.name.as_ref()); + #[cfg(not(feature = "debug"))] + return ShortName(FEATURE_DISABLED); + } + + /// Return the string hold by this `DebugName` + /// + /// This is intended for debugging purpose, and only available if the `debug` feature is enabled + #[cfg(feature = "debug")] + pub fn as_string(&self) -> String { + self.name.clone().into_owned() + } +} + +impl From> for DebugName { + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] + fn from(value: Cow<'static, str>) -> Self { + Self { + #[cfg(feature = "debug")] + name: value, + } + } +} + +impl From for DebugName { + fn from(value: String) -> Self { + Self::owned(value) + } +} + +impl From<&'static str> for DebugName { + fn from(value: &'static str) -> Self { + Self::borrowed(value) + } +} diff --git a/crates/bevy_utils/src/lib.rs b/crates/bevy_utils/src/lib.rs index e3bb07a512..58979139bb 100644 --- a/crates/bevy_utils/src/lib.rs +++ b/crates/bevy_utils/src/lib.rs @@ -43,12 +43,14 @@ cfg::parallel! { /// /// This includes the most common types in this crate, re-exported for your convenience. pub mod prelude { + pub use crate::debug_info::DebugName; pub use crate::default; } #[cfg(feature = "wgpu_wrapper")] mod wgpu_wrapper; +mod debug_info; mod default; mod once; diff --git a/crates/bevy_utils/src/map.rs b/crates/bevy_utils/src/map.rs index ca74e34dbb..3b54a357aa 100644 --- a/crates/bevy_utils/src/map.rs +++ b/crates/bevy_utils/src/map.rs @@ -1,7 +1,7 @@ use core::{any::TypeId, hash::Hash}; use bevy_platform::{ - collections::HashMap, + collections::{hash_map::Entry, HashMap}, hash::{Hashed, NoOpHash, PassHash}, }; @@ -38,6 +38,78 @@ impl PreHashMapExt for PreHashMap = HashMap; +/// Extension trait to make use of [`TypeIdMap`] more ergonomic. +/// +/// Each function on this trait is a trivial wrapper for a function +/// on [`HashMap`], replacing a `TypeId` key with a +/// generic parameter `T`. +/// +/// # Examples +/// +/// ```rust +/// # use std::any::TypeId; +/// # use bevy_utils::TypeIdMap; +/// use bevy_utils::TypeIdMapExt; +/// +/// struct MyType; +/// +/// // Using the built-in `HashMap` functions requires manually looking up `TypeId`s. +/// let mut map = TypeIdMap::default(); +/// map.insert(TypeId::of::(), 7); +/// assert_eq!(map.get(&TypeId::of::()), Some(&7)); +/// +/// // Using `TypeIdMapExt` functions does the lookup for you. +/// map.insert_type::(7); +/// assert_eq!(map.get_type::(), Some(&7)); +/// ``` +pub trait TypeIdMapExt { + /// Inserts a value for the type `T`. + /// + /// If the map did not previously contain this key then [`None`] is returned, + /// otherwise the value for this key is updated and the old value returned. + fn insert_type(&mut self, v: V) -> Option; + + /// Returns a reference to the value for type `T`, if one exists. + fn get_type(&self) -> Option<&V>; + + /// Returns a mutable reference to the value for type `T`, if one exists. + fn get_type_mut(&mut self) -> Option<&mut V>; + + /// Removes type `T` from the map, returning the value for this + /// key if it was previously present. + fn remove_type(&mut self) -> Option; + + /// Gets the type `T`'s entry in the map for in-place manipulation. + fn entry_type(&mut self) -> Entry<'_, TypeId, V, NoOpHash>; +} + +impl TypeIdMapExt for TypeIdMap { + #[inline] + fn insert_type(&mut self, v: V) -> Option { + self.insert(TypeId::of::(), v) + } + + #[inline] + fn get_type(&self) -> Option<&V> { + self.get(&TypeId::of::()) + } + + #[inline] + fn get_type_mut(&mut self) -> Option<&mut V> { + self.get_mut(&TypeId::of::()) + } + + #[inline] + fn remove_type(&mut self) -> Option { + self.remove(&TypeId::of::()) + } + + #[inline] + fn entry_type(&mut self) -> Entry<'_, TypeId, V, NoOpHash> { + self.entry(TypeId::of::()) + } +} + #[cfg(test)] mod tests { use super::*; @@ -67,7 +139,7 @@ mod tests { #[test] fn stable_hash_within_same_program_execution() { use alloc::vec::Vec; - + let mut map_1 = >::default(); let mut map_2 = >::default(); for i in 1..10 { diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index fb8f1fb18f..22e657cf03 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -57,6 +57,7 @@ impl Default for WindowPlugin { fn default() -> Self { WindowPlugin { primary_window: Some(Window::default()), + primary_cursor_options: Some(CursorOptions::default()), exit_condition: ExitCondition::OnAllClosed, close_when_requested: true, } @@ -76,6 +77,13 @@ pub struct WindowPlugin { /// [`exit_on_all_closed`]. pub primary_window: Option, + /// Settings for the cursor on the primary window. + /// + /// Defaults to `Some(CursorOptions::default())`. + /// + /// Has no effect if [`WindowPlugin::primary_window`] is `None`. + pub primary_cursor_options: Option, + /// Whether to exit the app when there are no open windows. /// /// If disabling this, ensure that you send the [`bevy_app::AppExit`] @@ -122,10 +130,14 @@ impl Plugin for WindowPlugin { .add_event::(); if let Some(primary_window) = &self.primary_window { - app.world_mut().spawn(primary_window.clone()).insert(( + let mut entity_commands = app.world_mut().spawn(primary_window.clone()); + entity_commands.insert(( PrimaryWindow, RawHandleWrapperHolder(Arc::new(Mutex::new(None))), )); + if let Some(primary_cursor_options) = &self.primary_cursor_options { + entity_commands.insert(primary_cursor_options.clone()); + } } match self.exit_condition { @@ -168,7 +180,8 @@ impl Plugin for WindowPlugin { // Register window descriptor and related types #[cfg(feature = "bevy_reflect")] app.register_type::() - .register_type::(); + .register_type::() + .register_type::(); } } diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 4f7d7b2f7b..e2a9ca3c0f 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -158,10 +158,8 @@ impl ContainsEntity for NormalizedWindowRef { all(feature = "serialize", feature = "bevy_reflect"), reflect(Serialize, Deserialize) )] +#[require(CursorOptions)] pub struct Window { - /// The cursor options of this window. Cursor icons are set with the `Cursor` component on the - /// window entity. - pub cursor_options: CursorOptions, /// What presentation mode to give the window. pub present_mode: PresentMode, /// Which fullscreen or windowing mode should be used. @@ -470,7 +468,6 @@ impl Default for Window { Self { title: DEFAULT_WINDOW_TITLE.to_owned(), name: None, - cursor_options: Default::default(), present_mode: Default::default(), mode: Default::default(), position: Default::default(), @@ -728,11 +725,11 @@ impl WindowResizeConstraints { } /// Cursor data for a [`Window`]. -#[derive(Debug, Clone)] +#[derive(Component, Debug, Clone)] #[cfg_attr( feature = "bevy_reflect", derive(Reflect), - reflect(Debug, Default, Clone) + reflect(Component, Debug, Default, Clone) )] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr( diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 6d814d4ac2..8926095dc0 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -25,8 +25,8 @@ use winit::{event_loop::EventLoop, window::WindowId}; use bevy_a11y::AccessibilityRequested; use bevy_app::{App, Last, Plugin}; use bevy_ecs::prelude::*; -use bevy_window::{exit_on_all_closed, Window, WindowCreated}; -use system::{changed_windows, check_keyboard_focus_lost, despawn_windows}; +use bevy_window::{exit_on_all_closed, CursorOptions, Window, WindowCreated}; +use system::{changed_cursor_options, changed_windows, check_keyboard_focus_lost, despawn_windows}; pub use system::{create_monitors, create_windows}; #[cfg(all(target_family = "wasm", target_os = "unknown"))] pub use winit::platform::web::CustomCursorExtWebSys; @@ -142,6 +142,7 @@ impl Plugin for WinitPlugin { // `exit_on_all_closed` only checks if windows exist but doesn't access data, // so we don't need to care about its ordering relative to `changed_windows` changed_windows.ambiguous_with(exit_on_all_closed), + changed_cursor_options, despawn_windows, check_keyboard_focus_lost, ) @@ -211,6 +212,7 @@ pub type CreateWindowParams<'w, 's, F = ()> = ( ( Entity, &'static mut Window, + &'static CursorOptions, Option<&'static RawHandleWrapperHolder>, ), F, diff --git a/crates/bevy_winit/src/state.rs b/crates/bevy_winit/src/state.rs index 934de5dad7..5b873d4620 100644 --- a/crates/bevy_winit/src/state.rs +++ b/crates/bevy_winit/src/state.rs @@ -46,7 +46,7 @@ use bevy_window::{ WindowScaleFactorChanged, WindowThemeChanged, }; #[cfg(target_os = "android")] -use bevy_window::{PrimaryWindow, RawHandleWrapper}; +use bevy_window::{CursorOptions, PrimaryWindow, RawHandleWrapper}; use crate::{ accessibility::ACCESS_KIT_ADAPTERS, @@ -474,7 +474,7 @@ impl ApplicationHandler for WinitAppRunnerState { if let Ok((window_component, mut cache)) = windows.get_mut(self.world_mut(), window) { if window_component.is_changed() { - cache.window = window_component.clone(); + **cache = window_component.clone(); } } }); @@ -605,10 +605,12 @@ impl WinitAppRunnerState { { // Get windows that are cached but without raw handles. Those window were already created, but got their // handle wrapper removed when the app was suspended. + let mut query = self.world_mut() - .query_filtered::<(Entity, &Window), (With, Without)>(); - if let Ok((entity, window)) = query.single(&self.world()) { + .query_filtered::<(Entity, &Window, &CursorOptions), (With, Without)>(); + if let Ok((entity, window, cursor_options)) = query.single(&self.world()) { let window = window.clone(); + let cursor_options = cursor_options.clone(); WINIT_WINDOWS.with_borrow_mut(|winit_windows| { ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| { @@ -622,6 +624,7 @@ impl WinitAppRunnerState { event_loop, entity, &window, + &cursor_options, adapters, &mut handlers, &accessibility_requested, diff --git a/crates/bevy_winit/src/system.rs b/crates/bevy_winit/src/system.rs index 873949ea89..3cc7c5a5f6 100644 --- a/crates/bevy_winit/src/system.rs +++ b/crates/bevy_winit/src/system.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; +use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ + change_detection::DetectChangesMut, entity::Entity, event::EventWriter, lifecycle::RemovedComponents, @@ -10,9 +12,9 @@ use bevy_ecs::{ }; use bevy_input::keyboard::{Key, KeyCode, KeyboardFocusLost, KeyboardInput}; use bevy_window::{ - ClosingWindow, Monitor, PrimaryMonitor, RawHandleWrapper, VideoMode, Window, WindowClosed, - WindowClosing, WindowCreated, WindowEvent, WindowFocused, WindowMode, WindowResized, - WindowWrapper, + ClosingWindow, CursorOptions, Monitor, PrimaryMonitor, RawHandleWrapper, VideoMode, Window, + WindowClosed, WindowClosing, WindowCreated, WindowEvent, WindowFocused, WindowMode, + WindowResized, WindowWrapper, }; use tracing::{error, info, warn}; @@ -59,7 +61,7 @@ pub fn create_windows( ) { WINIT_WINDOWS.with_borrow_mut(|winit_windows| { ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| { - for (entity, mut window, handle_holder) in &mut created_windows { + for (entity, mut window, cursor_options, handle_holder) in &mut created_windows { if winit_windows.get_window(entity).is_some() { continue; } @@ -70,6 +72,7 @@ pub fn create_windows( event_loop, entity, &window, + cursor_options, adapters, &mut handlers, &accessibility_requested, @@ -85,9 +88,8 @@ pub fn create_windows( .set_scale_factor_and_apply_to_physical_size(winit_window.scale_factor() as f32); commands.entity(entity).insert(( - CachedWindow { - window: window.clone(), - }, + CachedWindow(window.clone()), + CachedCursorOptions(cursor_options.clone()), WinitWindowPressedKeys::default(), )); @@ -281,10 +283,12 @@ pub(crate) fn despawn_windows( } /// The cached state of the window so we can check which properties were changed from within the app. -#[derive(Debug, Clone, Component)] -pub struct CachedWindow { - pub window: Window, -} +#[derive(Debug, Clone, Component, Deref, DerefMut)] +pub(crate) struct CachedWindow(Window); + +/// The cached state of the window so we can check which properties were changed from within the app. +#[derive(Debug, Clone, Component, Deref, DerefMut)] +pub(crate) struct CachedCursorOptions(CursorOptions); /// Propagates changes from [`Window`] entities to the [`winit`] backend. /// @@ -306,11 +310,11 @@ pub(crate) fn changed_windows( continue; }; - if window.title != cache.window.title { + if window.title != cache.title { winit_window.set_title(window.title.as_str()); } - if window.mode != cache.window.mode { + if window.mode != cache.mode { let new_mode = match window.mode { WindowMode::BorderlessFullscreen(monitor_selection) => { Some(Some(winit::window::Fullscreen::Borderless(select_monitor( @@ -352,15 +356,15 @@ pub(crate) fn changed_windows( } } - if window.resolution != cache.window.resolution { + if window.resolution != cache.resolution { let mut physical_size = PhysicalSize::new( window.resolution.physical_width(), window.resolution.physical_height(), ); let cached_physical_size = PhysicalSize::new( - cache.window.physical_width(), - cache.window.physical_height(), + cache.physical_width(), + cache.physical_height(), ); let base_scale_factor = window.resolution.base_scale_factor(); @@ -368,12 +372,12 @@ pub(crate) fn changed_windows( // Note: this may be different from `winit`'s base scale factor if // `scale_factor_override` is set to Some(f32) let scale_factor = window.scale_factor(); - let cached_scale_factor = cache.window.scale_factor(); + let cached_scale_factor = cache.scale_factor(); // Check and update `winit`'s physical size only if the window is not maximized if scale_factor != cached_scale_factor && !winit_window.is_maximized() { let logical_size = - if let Some(cached_factor) = cache.window.resolution.scale_factor_override() { + if let Some(cached_factor) = cache.resolution.scale_factor_override() { physical_size.to_logical::(cached_factor as f64) } else { physical_size.to_logical::(base_scale_factor as f64) @@ -397,7 +401,7 @@ pub(crate) fn changed_windows( } } - if window.physical_cursor_position() != cache.window.physical_cursor_position() { + if window.physical_cursor_position() != cache.physical_cursor_position() { if let Some(physical_position) = window.physical_cursor_position() { let position = PhysicalPosition::new(physical_position.x, physical_position.y); @@ -407,44 +411,23 @@ pub(crate) fn changed_windows( } } - if window.cursor_options.grab_mode != cache.window.cursor_options.grab_mode - && crate::winit_windows::attempt_grab(winit_window, window.cursor_options.grab_mode) - .is_err() - { - window.cursor_options.grab_mode = cache.window.cursor_options.grab_mode; - } - - if window.cursor_options.visible != cache.window.cursor_options.visible { - winit_window.set_cursor_visible(window.cursor_options.visible); - } - - if window.cursor_options.hit_test != cache.window.cursor_options.hit_test { - if let Err(err) = winit_window.set_cursor_hittest(window.cursor_options.hit_test) { - window.cursor_options.hit_test = cache.window.cursor_options.hit_test; - warn!( - "Could not set cursor hit test for window {}: {}", - window.title, err - ); - } - } - - if window.decorations != cache.window.decorations + if window.decorations != cache.decorations && window.decorations != winit_window.is_decorated() { winit_window.set_decorations(window.decorations); } - if window.resizable != cache.window.resizable + if window.resizable != cache.resizable && window.resizable != winit_window.is_resizable() { winit_window.set_resizable(window.resizable); } - if window.enabled_buttons != cache.window.enabled_buttons { + if window.enabled_buttons != cache.enabled_buttons { winit_window.set_enabled_buttons(convert_enabled_buttons(window.enabled_buttons)); } - if window.resize_constraints != cache.window.resize_constraints { + if window.resize_constraints != cache.resize_constraints { let constraints = window.resize_constraints.check_constraints(); let min_inner_size = LogicalSize { width: constraints.min_width, @@ -461,7 +444,7 @@ pub(crate) fn changed_windows( } } - if window.position != cache.window.position { + if window.position != cache.position { if let Some(position) = crate::winit_window_position( &window.position, &window.resolution, @@ -502,62 +485,62 @@ pub(crate) fn changed_windows( } } - if window.focused != cache.window.focused && window.focused { + if window.focused != cache.focused && window.focused { winit_window.focus_window(); } - if window.window_level != cache.window.window_level { + if window.window_level != cache.window_level { winit_window.set_window_level(convert_window_level(window.window_level)); } // Currently unsupported changes - if window.transparent != cache.window.transparent { - window.transparent = cache.window.transparent; + if window.transparent != cache.transparent { + window.transparent = cache.transparent; warn!("Winit does not currently support updating transparency after window creation."); } #[cfg(target_arch = "wasm32")] - if window.canvas != cache.window.canvas { - window.canvas.clone_from(&cache.window.canvas); + if window.canvas != cache.canvas { + window.canvas.clone_from(&cache.canvas); warn!( "Bevy currently doesn't support modifying the window canvas after initialization." ); } - if window.ime_enabled != cache.window.ime_enabled { + if window.ime_enabled != cache.ime_enabled { winit_window.set_ime_allowed(window.ime_enabled); } - if window.ime_position != cache.window.ime_position { + if window.ime_position != cache.ime_position { winit_window.set_ime_cursor_area( LogicalPosition::new(window.ime_position.x, window.ime_position.y), PhysicalSize::new(10, 10), ); } - if window.window_theme != cache.window.window_theme { + if window.window_theme != cache.window_theme { winit_window.set_theme(window.window_theme.map(convert_window_theme)); } - if window.visible != cache.window.visible { + if window.visible != cache.visible { winit_window.set_visible(window.visible); } #[cfg(target_os = "ios")] { - if window.recognize_pinch_gesture != cache.window.recognize_pinch_gesture { + if window.recognize_pinch_gesture != cache.recognize_pinch_gesture { winit_window.recognize_pinch_gesture(window.recognize_pinch_gesture); } - if window.recognize_rotation_gesture != cache.window.recognize_rotation_gesture { + if window.recognize_rotation_gesture != cache.recognize_rotation_gesture { winit_window.recognize_rotation_gesture(window.recognize_rotation_gesture); } - if window.recognize_doubletap_gesture != cache.window.recognize_doubletap_gesture { + if window.recognize_doubletap_gesture != cache.recognize_doubletap_gesture { winit_window.recognize_doubletap_gesture(window.recognize_doubletap_gesture); } - if window.recognize_pan_gesture != cache.window.recognize_pan_gesture { + if window.recognize_pan_gesture != cache.recognize_pan_gesture { match ( window.recognize_pan_gesture, - cache.window.recognize_pan_gesture, + cache.recognize_pan_gesture, ) { (Some(_), Some(_)) => { warn!("Bevy currently doesn't support modifying PanGesture number of fingers recognition. Please disable it before re-enabling it with the new number of fingers"); @@ -567,16 +550,15 @@ pub(crate) fn changed_windows( } } - if window.prefers_home_indicator_hidden != cache.window.prefers_home_indicator_hidden { + if window.prefers_home_indicator_hidden != cache.prefers_home_indicator_hidden { winit_window .set_prefers_home_indicator_hidden(window.prefers_home_indicator_hidden); } - if window.prefers_status_bar_hidden != cache.window.prefers_status_bar_hidden { + if window.prefers_status_bar_hidden != cache.prefers_status_bar_hidden { winit_window.set_prefers_status_bar_hidden(window.prefers_status_bar_hidden); } if window.preferred_screen_edges_deferring_system_gestures != cache - .window .preferred_screen_edges_deferring_system_gestures { use crate::converters::convert_screen_edge; @@ -585,7 +567,59 @@ pub(crate) fn changed_windows( winit_window.set_preferred_screen_edges_deferring_system_gestures(preferred_edge); } } - cache.window = window.clone(); + **cache = window.clone(); + } + }); +} + +pub(crate) fn changed_cursor_options( + mut changed_windows: Query< + ( + Entity, + &Window, + &mut CursorOptions, + &mut CachedCursorOptions, + ), + Changed, + >, + _non_send_marker: NonSendMarker, +) { + WINIT_WINDOWS.with_borrow(|winit_windows| { + for (entity, window, mut cursor_options, mut cache) in &mut changed_windows { + // This system already only runs when the cursor options change, so we need to bypass change detection or the next frame will also run this system + let cursor_options = cursor_options.bypass_change_detection(); + let Some(winit_window) = winit_windows.get_window(entity) else { + continue; + }; + // Don't check the cache for the grab mode. It can change through external means, leaving the cache outdated. + if let Err(err) = + crate::winit_windows::attempt_grab(winit_window, cursor_options.grab_mode) + { + warn!( + "Could not set cursor grab mode for window {}: {}", + window.title, err + ); + cursor_options.grab_mode = cache.grab_mode; + } else { + cache.grab_mode = cursor_options.grab_mode; + } + + if cursor_options.visible != cache.visible { + winit_window.set_cursor_visible(cursor_options.visible); + cache.visible = cursor_options.visible; + } + + if cursor_options.hit_test != cache.hit_test { + if let Err(err) = winit_window.set_cursor_hittest(cursor_options.hit_test) { + warn!( + "Could not set cursor hit test for window {}: {}", + window.title, err + ); + cursor_options.hit_test = cache.hit_test; + } else { + cache.hit_test = cursor_options.hit_test; + } + } } }); } diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index 8bf326f453..5110e670c2 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -4,8 +4,8 @@ use bevy_ecs::entity::Entity; use bevy_ecs::entity::EntityHashMap; use bevy_platform::collections::HashMap; use bevy_window::{ - CursorGrabMode, MonitorSelection, VideoModeSelection, Window, WindowMode, WindowPosition, - WindowResolution, WindowWrapper, + CursorGrabMode, CursorOptions, MonitorSelection, VideoModeSelection, Window, WindowMode, + WindowPosition, WindowResolution, WindowWrapper, }; use tracing::warn; @@ -58,6 +58,7 @@ impl WinitWindows { event_loop: &ActiveEventLoop, entity: Entity, window: &Window, + cursor_options: &CursorOptions, adapters: &mut AccessKitAdapters, handlers: &mut WinitActionRequestHandlers, accessibility_requested: &AccessibilityRequested, @@ -310,16 +311,16 @@ impl WinitWindows { winit_window.set_visible(window.visible); // Do not set the grab mode on window creation if it's none. It can fail on mobile. - if window.cursor_options.grab_mode != CursorGrabMode::None { - let _ = attempt_grab(&winit_window, window.cursor_options.grab_mode); + if cursor_options.grab_mode != CursorGrabMode::None { + let _ = attempt_grab(&winit_window, cursor_options.grab_mode); } - winit_window.set_cursor_visible(window.cursor_options.visible); + winit_window.set_cursor_visible(cursor_options.visible); // Do not set the cursor hittest on window creation if it's false, as it will always fail on // some platforms and log an unfixable warning. - if !window.cursor_options.hit_test { - if let Err(err) = winit_window.set_cursor_hittest(window.cursor_options.hit_test) { + if !cursor_options.hit_test { + if let Err(err) = winit_window.set_cursor_hittest(cursor_options.hit_test) { warn!( "Could not set cursor hit test for window {}: {}", window.title, err diff --git a/docs/cargo_features.md b/docs/cargo_features.md index 52e9441688..5998d8cb90 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -41,6 +41,7 @@ The default feature set enables most of the expected features of a game engine, |bevy_window|Windowing layer| |bevy_winit|winit window and input backend| |custom_cursor|Enable winit custom cursor support| +|debug|Enable collecting debug information about systems and components to help with diagnostics| |default_font|Include a default font, containing only ASCII characters, at the cost of a 20kB binary size increase| |hdr|HDR image format support| |ktx2|KTX2 compressed texture support| diff --git a/examples/ecs/custom_query_param.rs b/examples/ecs/custom_query_param.rs index 39b3e6f072..8960557ef4 100644 --- a/examples/ecs/custom_query_param.rs +++ b/examples/ecs/custom_query_param.rs @@ -137,7 +137,7 @@ fn print_components_iter_mut( println!("Print components (iter_mut):"); for e in &mut query { // Re-declaring the variable to illustrate the type of the actual iterator item. - let e: CustomQueryItem<'_, _, _> = e; + let e: CustomQueryItem<'_, '_, _, _> = e; println!("Entity: {}", e.entity); println!("A: {:?}", e.a); println!("B: {:?}", e.b); @@ -155,7 +155,7 @@ fn print_components_iter( println!("Print components (iter):"); for e in &query { // Re-declaring the variable to illustrate the type of the actual iterator item. - let e: CustomQueryReadOnlyItem<'_, _, _> = e; + let e: CustomQueryReadOnlyItem<'_, '_, _, _> = e; println!("Entity: {}", e.entity); println!("A: {:?}", e.a); println!("B: {:?}", e.b); diff --git a/examples/games/desk_toy.rs b/examples/games/desk_toy.rs index c25286dd9c..b5c638348e 100644 --- a/examples/games/desk_toy.rs +++ b/examples/games/desk_toy.rs @@ -10,7 +10,7 @@ use bevy::{ app::AppExit, input::common_conditions::{input_just_pressed, input_just_released}, prelude::*, - window::{PrimaryWindow, WindowLevel}, + window::{CursorOptions, PrimaryWindow, WindowLevel}, }; #[cfg(target_os = "macos")] @@ -219,12 +219,13 @@ fn get_cursor_world_pos( /// Update whether the window is clickable or not fn update_cursor_hit_test( cursor_world_pos: Res, - mut primary_window: Single<&mut Window, With>, + primary_window: Single<(&Window, &mut CursorOptions), With>, bevy_logo_transform: Single<&Transform, With>, ) { + let (window, mut cursor_options) = primary_window.into_inner(); // If the window has decorations (e.g. a border) then it should be clickable - if primary_window.decorations { - primary_window.cursor_options.hit_test = true; + if window.decorations { + cursor_options.hit_test = true; return; } @@ -234,7 +235,7 @@ fn update_cursor_hit_test( }; // If the cursor is within the radius of the Bevy logo make the window clickable otherwise the window is not clickable - primary_window.cursor_options.hit_test = bevy_logo_transform + cursor_options.hit_test = bevy_logo_transform .translation .truncate() .distance(cursor_world_pos) diff --git a/examples/games/stepping.rs b/examples/games/stepping.rs index 653b7a12ff..dce37d0842 100644 --- a/examples/games/stepping.rs +++ b/examples/games/stepping.rs @@ -104,7 +104,10 @@ fn build_ui( mut state: ResMut, ) { let mut text_spans = Vec::new(); - let mut always_run = Vec::new(); + let mut always_run: Vec<( + bevy_ecs::intern::Interned, + NodeId, + )> = Vec::new(); let Ok(schedule_order) = stepping.schedules() else { return; @@ -131,7 +134,8 @@ fn build_ui( for (node_id, system) in systems { // skip bevy default systems; we don't want to step those - if system.name().starts_with("bevy") { + #[cfg(feature = "debug")] + if system.name().as_string().starts_with("bevy") { always_run.push((*label, node_id)); continue; } diff --git a/examples/helpers/camera_controller.rs b/examples/helpers/camera_controller.rs index a60aa69a5e..3f6e4ed477 100644 --- a/examples/helpers/camera_controller.rs +++ b/examples/helpers/camera_controller.rs @@ -8,7 +8,7 @@ use bevy::{ input::mouse::{AccumulatedMouseMotion, AccumulatedMouseScroll, MouseScrollUnit}, prelude::*, - window::CursorGrabMode, + window::{CursorGrabMode, CursorOptions}, }; use std::{f32::consts::*, fmt}; @@ -126,7 +126,7 @@ Freecam Controls: fn run_camera_controller( time: Res