mirror of
https://github.com/bevyengine/bevy
synced 2024-12-28 05:53:07 +00:00
ec8c8fbc8a
# Objective Supercedes #6452. Upon inspection of the [generated assembly](https://gist.github.com/james7132/c2740c6941b80d7912f1e8888e223cbb#file-original-s) of a [simple Bevy binary](https://gist.github.com/james7132/c2740c6941b80d7912f1e8888e223cbb#file-source-rs) compiled with `cargo rustc --release -- --emit asm`, it's apparent that there are multiple unnecessary branches in the generated assembly: ```assembly .LBB5_5: cmpq %r10, %r11 je .LBB5_15 movq (%r11), %rcx movq 328(%r15), %rdx cmpq %rdx, %rcx jae .LBB5_14 movq 312(%r15), %rdi leaq (%rcx,%rcx,2), %rcx shlq $5, %rcx movq 336(%r12), %rdx movq 64(%rdi,%rcx), %rax cmpq %rdx, %rax jbe .LBB5_4 leaq (%rdi,%rcx), %rsi movq 48(%rsi), %rbp shlq $4, %rdx cmpq $0, (%rbp,%rdx) je .LBB5_4 movq 344(%r12), %rbx cmpq %rbx, %rax jbe .LBB5_4 shlq $4, %rbx cmpq $0, (%rbp,%rbx) je .LBB5_4 addq $8, %r11 movq 88(%rdi,%rcx), %rcx testq %rcx, %rcx je .LBB5_5 movq (%rsi), %rax movq 8(%rbp,%rdx), %rdx leaq (%rdx,%rdx,4), %rdi shlq $4, %rdi movq 32(%rax,%rdi), %rdx movq 56(%rax,%rdi), %r8 movq 8(%rbp,%rbx), %rbp leaq (%rbp,%rbp,4), %rbp shlq $4, %rbp movq 32(%rax,%rbp), %r9 xorl %ebp, %ebp jmp .LBB5_13 .p2align 4, 0x90 ``` Almost every one of the instructions starting with `j` is a potential branch, which can significantly slow down accesses. Of these, two labels are both common and never used: ```asm .LBB5_14: leaq __unnamed_2(%rip), %r8 callq _ZN4core9panicking18panic_bounds_check17h70367088e72af65aE ud2 .LBB5_4: callq _ZN8bevy_ecs5query25debug_checked_unreachable17h0855ff520ceaea77E ud2 .seh_endproc ``` These correpsond to subprocedure calls to panicking due to out of bounds from indexing `Tables` and `debug_checked_unreadable`. Both of which should be inlined and optimized out, but are not. ## Solution Make `debug_checked_unreachable` a macro to forcibly inline either `unreachable!()` in debug builds, and `std::hint::unreachable_unchecked()` in release mode. Replace the `Tables` and `Archetype` index access with `get(id).unwrap_or_else(|| debug_checked_unreachable!())` to assume that the table or archetype provided exists. This has no external breaking change of any kind. The equivalent section of code with these changes removes most of the conditional jump instructions: ```asm .LBB5_5: movss (%rbx,%rbp,4), %xmm0 movl %r14d, 4(%r8,%rbp,8) addss (%rdi,%rbp,4), %xmm0 movss %xmm0, (%rdi,%rbp,4) incq %rbp .LBB5_1: cmpq %rdx, %rbp jne .LBB5_5 .p2align 4, 0x90 .LBB5_2: cmpq %rcx, %rax je .LBB5_6 movq (%rax), %rdx addq $8, %rax movq 312(%rsi), %rbp leaq (%rdx,%rdx,2), %rbx shlq $5, %rbx movq 88(%rbp,%rbx), %rdx testq %rdx, %rdx je .LBB5_2 leaq (%rbx,%rbp), %r8 movq 336(%r15), %rdi movq 344(%r15), %r9 movq 48(%rbp,%rbx), %r10 shlq $4, %rdi movq (%r8), %rbx movq 8(%r10,%rdi), %rdi leaq (%rdi,%rdi,4), %rbp shlq $4, %rbp movq 32(%rbx,%rbp), %rdi movq 56(%rbx,%rbp), %r8 shlq $4, %r9 movq 8(%r10,%r9), %rbp leaq (%rbp,%rbp,4), %rbp shlq $4, %rbp movq 32(%rbx,%rbp), %rbx xorl %ebp, %ebp jmp .LBB5_5 .LBB5_6: addq $40, %rsp popq %rbx popq %rbp popq %rdi popq %rsi popq %r14 popq %r15 retq .seh_endproc ``` ## Performance Microbenchmarks results: <details> ``` group main no-panic-query ----- ---- -------------- busy_systems/01x_entities_03_systems 1.20 42.4±2.66µs ? ?/sec 1.00 35.3±1.68µs ? ?/sec busy_systems/01x_entities_06_systems 1.32 83.8±3.50µs ? ?/sec 1.00 63.6±1.72µs ? ?/sec busy_systems/01x_entities_09_systems 1.15 113.3±8.90µs ? ?/sec 1.00 98.2±6.15µs ? ?/sec busy_systems/01x_entities_12_systems 1.27 160.8±32.44µs ? ?/sec 1.00 126.6±4.70µs ? ?/sec busy_systems/01x_entities_15_systems 1.12 179.6±3.71µs ? ?/sec 1.00 160.3±11.03µs ? ?/sec busy_systems/02x_entities_03_systems 1.18 76.8±3.14µs ? ?/sec 1.00 65.2±3.17µs ? ?/sec busy_systems/02x_entities_06_systems 1.16 144.6±6.10µs ? ?/sec 1.00 124.5±5.14µs ? ?/sec busy_systems/02x_entities_09_systems 1.19 215.3±9.18µs ? ?/sec 1.00 181.5±5.67µs ? ?/sec busy_systems/02x_entities_12_systems 1.20 266.7±8.33µs ? ?/sec 1.00 222.0±9.53µs ? ?/sec busy_systems/02x_entities_15_systems 1.23 338.8±10.53µs ? ?/sec 1.00 276.3±6.94µs ? ?/sec busy_systems/03x_entities_03_systems 1.43 113.5±5.06µs ? ?/sec 1.00 79.6±1.49µs ? ?/sec busy_systems/03x_entities_06_systems 1.38 217.3±12.67µs ? ?/sec 1.00 157.5±3.07µs ? ?/sec busy_systems/03x_entities_09_systems 1.23 308.8±24.75µs ? ?/sec 1.00 251.6±8.93µs ? ?/sec busy_systems/03x_entities_12_systems 1.05 347.7±12.43µs ? ?/sec 1.00 330.6±11.43µs ? ?/sec busy_systems/03x_entities_15_systems 1.13 455.5±13.88µs ? ?/sec 1.00 401.7±17.29µs ? ?/sec busy_systems/04x_entities_03_systems 1.24 144.7±5.89µs ? ?/sec 1.00 116.9±6.29µs ? ?/sec busy_systems/04x_entities_06_systems 1.24 282.8±21.40µs ? ?/sec 1.00 228.6±21.31µs ? ?/sec busy_systems/04x_entities_09_systems 1.35 431.8±14.10µs ? ?/sec 1.00 319.6±9.83µs ? ?/sec busy_systems/04x_entities_12_systems 1.16 493.8±22.87µs ? ?/sec 1.00 424.9±15.24µs ? ?/sec busy_systems/04x_entities_15_systems 1.10 587.5±23.25µs ? ?/sec 1.00 531.7±16.32µs ? ?/sec busy_systems/05x_entities_03_systems 1.14 148.2±9.61µs ? ?/sec 1.00 129.5±4.32µs ? ?/sec busy_systems/05x_entities_06_systems 1.31 359.7±17.46µs ? ?/sec 1.00 273.6±10.55µs ? ?/sec busy_systems/05x_entities_09_systems 1.22 473.5±23.11µs ? ?/sec 1.00 389.3±13.62µs ? ?/sec busy_systems/05x_entities_12_systems 1.05 562.9±20.76µs ? ?/sec 1.00 536.5±24.35µs ? ?/sec busy_systems/05x_entities_15_systems 1.23 818.5±28.70µs ? ?/sec 1.00 666.6±45.87µs ? ?/sec contrived/01x_entities_03_systems 1.27 27.5±0.49µs ? ?/sec 1.00 21.6±1.71µs ? ?/sec contrived/01x_entities_06_systems 1.22 49.9±1.18µs ? ?/sec 1.00 40.7±2.62µs ? ?/sec contrived/01x_entities_09_systems 1.30 72.3±2.39µs ? ?/sec 1.00 55.4±2.60µs ? ?/sec contrived/01x_entities_12_systems 1.28 94.3±9.44µs ? ?/sec 1.00 73.7±3.62µs ? ?/sec contrived/01x_entities_15_systems 1.25 118.0±2.43µs ? ?/sec 1.00 94.1±3.99µs ? ?/sec contrived/02x_entities_03_systems 1.23 41.6±1.71µs ? ?/sec 1.00 33.7±2.30µs ? ?/sec contrived/02x_entities_06_systems 1.19 78.6±2.63µs ? ?/sec 1.00 65.9±2.35µs ? ?/sec contrived/02x_entities_09_systems 1.28 113.6±3.60µs ? ?/sec 1.00 88.6±3.60µs ? ?/sec contrived/02x_entities_12_systems 1.20 146.4±5.75µs ? ?/sec 1.00 121.7±3.35µs ? ?/sec contrived/02x_entities_15_systems 1.23 178.5±4.86µs ? ?/sec 1.00 145.7±4.00µs ? ?/sec contrived/03x_entities_03_systems 1.42 58.3±2.77µs ? ?/sec 1.00 41.1±1.54µs ? ?/sec contrived/03x_entities_06_systems 1.32 108.5±7.30µs ? ?/sec 1.00 82.4±4.86µs ? ?/sec contrived/03x_entities_09_systems 1.23 153.7±4.61µs ? ?/sec 1.00 125.0±4.76µs ? ?/sec contrived/03x_entities_12_systems 1.18 197.5±5.12µs ? ?/sec 1.00 166.8±8.14µs ? ?/sec contrived/03x_entities_15_systems 1.23 238.8±6.38µs ? ?/sec 1.00 194.6±4.55µs ? ?/sec contrived/04x_entities_03_systems 1.34 66.4±3.42µs ? ?/sec 1.00 49.5±1.98µs ? ?/sec contrived/04x_entities_06_systems 1.27 134.3±4.86µs ? ?/sec 1.00 105.8±3.58µs ? ?/sec contrived/04x_entities_09_systems 1.26 193.2±3.83µs ? ?/sec 1.00 153.0±5.60µs ? ?/sec contrived/04x_entities_12_systems 1.16 237.1±5.78µs ? ?/sec 1.00 204.9±18.77µs ? ?/sec contrived/04x_entities_15_systems 1.17 289.2±4.76µs ? ?/sec 1.00 246.3±8.57µs ? ?/sec contrived/05x_entities_03_systems 1.26 80.4±2.90µs ? ?/sec 1.00 63.7±3.07µs ? ?/sec contrived/05x_entities_06_systems 1.27 161.6±13.47µs ? ?/sec 1.00 127.2±5.59µs ? ?/sec contrived/05x_entities_09_systems 1.22 228.0±7.76µs ? ?/sec 1.00 186.2±7.68µs ? ?/sec contrived/05x_entities_12_systems 1.20 289.5±6.21µs ? ?/sec 1.00 241.8±7.52µs ? ?/sec contrived/05x_entities_15_systems 1.18 357.3±11.24µs ? ?/sec 1.00 302.7±7.21µs ? ?/sec heavy_compute/base 1.01 302.4±3.52µs ? ?/sec 1.00 300.2±3.40µs ? ?/sec iter_fragmented/base 1.00 348.1±7.51ns ? ?/sec 1.01 351.9±8.32ns ? ?/sec iter_fragmented/foreach 1.03 239.8±23.78ns ? ?/sec 1.00 233.8±18.12ns ? ?/sec iter_fragmented/foreach_wide 1.00 3.9±0.13µs ? ?/sec 1.02 4.0±0.22µs ? ?/sec iter_fragmented/wide 1.18 4.6±0.15µs ? ?/sec 1.00 3.9±0.10µs ? ?/sec iter_fragmented_sparse/base 1.02 8.1±0.15ns ? ?/sec 1.00 7.9±0.56ns ? ?/sec iter_fragmented_sparse/foreach 1.00 7.8±0.22ns ? ?/sec 1.01 7.9±0.62ns ? ?/sec iter_fragmented_sparse/foreach_wide 1.00 37.2±1.17ns ? ?/sec 1.10 40.9±0.95ns ? ?/sec iter_fragmented_sparse/wide 1.09 48.4±2.13ns ? ?/sec 1.00 44.5±18.34ns ? ?/sec iter_simple/base 1.02 8.4±0.10µs ? ?/sec 1.00 8.2±0.14µs ? ?/sec iter_simple/foreach 1.01 8.3±0.07µs ? ?/sec 1.00 8.2±0.09µs ? ?/sec iter_simple/foreach_sparse_set 1.00 25.3±0.32µs ? ?/sec 1.02 25.7±0.42µs ? ?/sec iter_simple/foreach_wide 1.03 41.1±0.94µs ? ?/sec 1.00 39.9±0.41µs ? ?/sec iter_simple/foreach_wide_sparse_set 1.05 123.6±2.05µs ? ?/sec 1.00 118.1±2.78µs ? ?/sec iter_simple/sparse_set 1.14 30.5±1.40µs ? ?/sec 1.00 26.9±0.64µs ? ?/sec iter_simple/system 1.01 8.4±0.25µs ? ?/sec 1.00 8.4±0.11µs ? ?/sec iter_simple/wide 1.18 48.2±0.62µs ? ?/sec 1.00 40.7±0.38µs ? ?/sec iter_simple/wide_sparse_set 1.12 140.8±21.56µs ? ?/sec 1.00 126.0±2.30µs ? ?/sec query_get/50000_entities_sparse 1.17 378.6±7.60µs ? ?/sec 1.00 324.1±23.17µs ? ?/sec query_get/50000_entities_table 1.08 330.9±10.90µs ? ?/sec 1.00 306.8±4.98µs ? ?/sec query_get_component/50000_entities_sparse 1.00 976.7±19.55µs ? ?/sec 1.00 979.8±35.87µs ? ?/sec query_get_component/50000_entities_table 1.00 1029.0±15.11µs ? ?/sec 1.05 1080.0±59.18µs ? ?/sec query_get_component_simple/system 1.13 839.7±14.18µs ? ?/sec 1.00 742.8±10.72µs ? ?/sec query_get_component_simple/unchecked 1.01 909.0±15.17µs ? ?/sec 1.00 898.0±13.56µs ? ?/sec query_get_many_10/50000_calls_sparse 1.04 5.5±0.54ms ? ?/sec 1.00 5.3±0.67ms ? ?/sec query_get_many_10/50000_calls_table 1.01 4.9±0.49ms ? ?/sec 1.00 4.8±0.45ms ? ?/sec query_get_many_2/50000_calls_sparse 1.28 848.4±210.89µs ? ?/sec 1.00 664.8±47.69µs ? ?/sec query_get_many_2/50000_calls_table 1.05 779.0±73.85µs ? ?/sec 1.00 739.2±83.02µs ? ?/sec query_get_many_5/50000_calls_sparse 1.05 2.4±0.37ms ? ?/sec 1.00 2.3±0.33ms ? ?/sec query_get_many_5/50000_calls_table 1.00 1939.9±75.22µs ? ?/sec 1.04 2.0±0.19ms ? ?/sec run_criteria/yes_using_query/001_systems 1.00 3.7±0.38µs ? ?/sec 1.30 4.9±0.14µs ? ?/sec run_criteria/yes_using_query/006_systems 1.00 8.9±0.40µs ? ?/sec 1.17 10.3±0.57µs ? ?/sec run_criteria/yes_using_query/011_systems 1.00 13.9±0.49µs ? ?/sec 1.08 15.0±0.89µs ? ?/sec run_criteria/yes_using_query/016_systems 1.00 18.8±0.74µs ? ?/sec 1.00 18.8±1.43µs ? ?/sec run_criteria/yes_using_query/021_systems 1.07 24.1±0.87µs ? ?/sec 1.00 22.6±1.58µs ? ?/sec run_criteria/yes_using_query/026_systems 1.04 27.9±0.62µs ? ?/sec 1.00 26.8±1.71µs ? ?/sec run_criteria/yes_using_query/031_systems 1.09 33.3±1.03µs ? ?/sec 1.00 30.5±2.18µs ? ?/sec run_criteria/yes_using_query/036_systems 1.14 38.7±0.80µs ? ?/sec 1.00 33.9±1.75µs ? ?/sec run_criteria/yes_using_query/041_systems 1.18 43.7±1.07µs ? ?/sec 1.00 37.0±2.39µs ? ?/sec run_criteria/yes_using_query/046_systems 1.14 47.6±1.16µs ? ?/sec 1.00 41.9±2.09µs ? ?/sec run_criteria/yes_using_query/051_systems 1.17 52.9±2.04µs ? ?/sec 1.00 45.3±1.75µs ? ?/sec run_criteria/yes_using_query/056_systems 1.25 59.2±2.38µs ? ?/sec 1.00 47.2±2.01µs ? ?/sec run_criteria/yes_using_query/061_systems 1.28 66.1±15.84µs ? ?/sec 1.00 51.5±2.47µs ? ?/sec run_criteria/yes_using_query/066_systems 1.28 70.2±2.57µs ? ?/sec 1.00 54.7±2.58µs ? ?/sec run_criteria/yes_using_query/071_systems 1.30 75.5±2.27µs ? ?/sec 1.00 58.2±3.31µs ? ?/sec run_criteria/yes_using_query/076_systems 1.26 81.5±2.66µs ? ?/sec 1.00 64.5±3.13µs ? ?/sec run_criteria/yes_using_query/081_systems 1.29 89.7±2.58µs ? ?/sec 1.00 69.3±3.47µs ? ?/sec run_criteria/yes_using_query/086_systems 1.33 95.6±3.39µs ? ?/sec 1.00 71.8±3.48µs ? ?/sec run_criteria/yes_using_query/091_systems 1.25 102.0±3.67µs ? ?/sec 1.00 81.4±4.82µs ? ?/sec run_criteria/yes_using_query/096_systems 1.33 111.7±3.29µs ? ?/sec 1.00 83.8±4.15µs ? ?/sec run_criteria/yes_using_query/101_systems 1.29 113.2±12.04µs ? ?/sec 1.00 87.7±5.15µs ? ?/sec world_query_for_each/50000_entities_sparse 1.00 47.4±0.51µs ? ?/sec 1.00 47.3±0.33µs ? ?/sec world_query_for_each/50000_entities_table 1.00 27.2±0.50µs ? ?/sec 1.00 27.2±0.17µs ? ?/sec world_query_get/50000_entities_sparse_wide 1.09 210.5±1.78µs ? ?/sec 1.00 192.5±2.61µs ? ?/sec world_query_get/50000_entities_table 1.00 127.7±2.09µs ? ?/sec 1.07 136.2±5.95µs ? ?/sec world_query_get/50000_entities_table_wide 1.00 209.8±2.37µs ? ?/sec 1.15 240.6±2.04µs ? ?/sec world_query_iter/50000_entities_sparse 1.00 54.2±0.36µs ? ?/sec 1.01 54.7±0.61µs ? ?/sec world_query_iter/50000_entities_table 1.00 27.2±0.31µs ? ?/sec 1.00 27.3±0.64µs ? ?/sec ``` </details> NOTE: This PR includes a change to enable LTO on our benchmarks to get a "fully optimized" baseline for our benchmarks. Both the main and the current PR's results were with LTO enabled.
1464 lines
49 KiB
Rust
1464 lines
49 KiB
Rust
use crate::{
|
|
archetype::{Archetype, ArchetypeComponentId},
|
|
change_detection::Ticks,
|
|
component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType},
|
|
entity::Entity,
|
|
query::{Access, DebugCheckedUnwrap, FilteredAccess},
|
|
storage::{ComponentSparseSet, Table},
|
|
world::{Mut, World},
|
|
};
|
|
use bevy_ecs_macros::all_tuples;
|
|
pub use bevy_ecs_macros::WorldQuery;
|
|
use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref};
|
|
use std::{cell::UnsafeCell, marker::PhantomData};
|
|
|
|
/// Types that can be fetched from a [`World`] using a [`Query`].
|
|
///
|
|
/// There are many types that natively implement this trait:
|
|
///
|
|
/// - **Component references.**
|
|
/// Fetches a component by reference (immutably or mutably).
|
|
/// - **`WorldQuery` tuples.**
|
|
/// If every element of a tuple implements `WorldQuery`, then the tuple itself also implements the same trait.
|
|
/// This enables a single `Query` to access multiple components and filter over multiple conditions.
|
|
/// Due to the current lack of variadic generics in Rust, the trait has been implemented for tuples from 0 to 15 elements,
|
|
/// but nesting of tuples allows infinite `WorldQuery`s.
|
|
/// - **Component filters.**
|
|
/// [`With`] and [`Without`] filters can be applied to check if the queried entity contains or not a particular component.
|
|
/// - **Change detection filters.**
|
|
/// [`Added`] and [`Changed`] filters can be applied to detect component changes to an entity.
|
|
/// - **Filter disjunction operator.**
|
|
/// By default, tuples compose query filters in such a way that all conditions must be satisfied to generate a query item for a given entity.
|
|
/// Wrapping a tuple inside an [`Or`] operator will relax the requirement to just one condition.
|
|
/// - **[`Entity`].**
|
|
/// Gets the identifier of the queried entity.
|
|
/// - **[`Option`].**
|
|
/// By default, a world query only tests entities that have the matching component types.
|
|
/// Wrapping it into an `Option` will increase the query search space, and it will return `None` if an entity doesn't satisfy the `WorldQuery`.
|
|
/// - **[`AnyOf`].**
|
|
/// Equivalent to wrapping each world query inside it into an `Option`.
|
|
/// - **[`ChangeTrackers`].**
|
|
/// Similar to change detection filters but it is used as a query fetch parameter.
|
|
/// It exposes methods to check for changes to the wrapped component.
|
|
///
|
|
/// Implementing the trait manually can allow for a fundamentally new type of behaviour.
|
|
///
|
|
/// # Trait derivation
|
|
///
|
|
/// Query design can be easily structured by deriving `WorldQuery` for custom types.
|
|
/// Despite the added complexity, this approach has several advantages over using `WorldQuery` tuples.
|
|
/// The most relevant improvements are:
|
|
///
|
|
/// - Reusability across multiple systems.
|
|
/// - There is no need to destructure a tuple since all fields are named.
|
|
/// - Subqueries can be composed together to create a more complex query.
|
|
/// - Methods can be implemented for the query items.
|
|
/// - There is no hardcoded limit on the number of elements.
|
|
///
|
|
/// This trait can only be derived if each field either
|
|
///
|
|
/// * also implements `WorldQuery`, or
|
|
/// * is marked with `#[world_query(ignore)]`. Fields decorated with this attribute
|
|
/// must implement [`Default`] and will be initialized to the default value as defined
|
|
/// by the trait.
|
|
///
|
|
/// The derive macro only supports regular structs (structs with named fields).
|
|
///
|
|
/// ```
|
|
/// # use bevy_ecs::prelude::*;
|
|
/// use bevy_ecs::query::WorldQuery;
|
|
/// #
|
|
/// # #[derive(Component)]
|
|
/// # struct ComponentA;
|
|
/// # #[derive(Component)]
|
|
/// # struct ComponentB;
|
|
///
|
|
/// #[derive(WorldQuery)]
|
|
/// struct MyQuery {
|
|
/// entity: Entity,
|
|
/// // It is required that all reference lifetimes are explicitly annotated, just like in any
|
|
/// // struct. Each lifetime should be 'static.
|
|
/// component_a: &'static ComponentA,
|
|
/// component_b: &'static ComponentB,
|
|
/// }
|
|
///
|
|
/// fn my_system(query: Query<MyQuery>) {
|
|
/// for q in &query {
|
|
/// q.component_a;
|
|
/// }
|
|
/// }
|
|
/// # bevy_ecs::system::assert_is_system(my_system);
|
|
/// ```
|
|
///
|
|
/// ## Macro expansion
|
|
///
|
|
/// Expanding the macro will declare three or six additional structs, depending on whether or not the struct is marked as mutable.
|
|
/// For a struct named `X`, the additional structs will be:
|
|
///
|
|
/// |Struct name|`mutable` only|Description|
|
|
/// |:---:|:---:|---|
|
|
/// |`XState`|---|Used as the [`State`] type for `X` and `XReadOnly`|
|
|
/// |`XItem`|---|The type of the query item for `X`|
|
|
/// |`XFetch`|---|Used as the [`Fetch`] type for `X`|
|
|
/// |`XReadOnlyItem`|✓|The type of the query item for `XReadOnly`|
|
|
/// |`XReadOnlyFetch`|✓|Used as the [`Fetch`] type for `XReadOnly`|
|
|
/// |`XReadOnly`|✓|[`ReadOnly`] variant of `X`|
|
|
///
|
|
/// ## Adding mutable references
|
|
///
|
|
/// Simply adding mutable references to a derived `WorldQuery` will result in a compilation error:
|
|
///
|
|
/// ```compile_fail
|
|
/// # use bevy_ecs::prelude::*;
|
|
/// # use bevy_ecs::query::WorldQuery;
|
|
/// #
|
|
/// # #[derive(Component)]
|
|
/// # struct ComponentA;
|
|
/// #
|
|
/// #[derive(WorldQuery)]
|
|
/// struct CustomQuery {
|
|
/// component_a: &'static mut ComponentA,
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// To grant mutable access to components, the struct must be marked with the `#[world_query(mutable)]` attribute.
|
|
/// This will also create three more structs that will be used for accessing the query immutably (see table above).
|
|
///
|
|
/// ```
|
|
/// # use bevy_ecs::prelude::*;
|
|
/// # use bevy_ecs::query::WorldQuery;
|
|
/// #
|
|
/// # #[derive(Component)]
|
|
/// # struct ComponentA;
|
|
/// #
|
|
/// #[derive(WorldQuery)]
|
|
/// #[world_query(mutable)]
|
|
/// struct CustomQuery {
|
|
/// component_a: &'static mut ComponentA,
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// ## Adding methods to query items
|
|
///
|
|
/// It is possible to add methods to query items in order to write reusable logic about related components.
|
|
/// This will often make systems more readable because low level logic is moved out from them.
|
|
/// It is done by adding `impl` blocks with methods for the `-Item` or `-ReadOnlyItem` generated structs.
|
|
///
|
|
/// ```
|
|
/// # use bevy_ecs::prelude::*;
|
|
/// # use bevy_ecs::query::WorldQuery;
|
|
/// #
|
|
/// #[derive(Component)]
|
|
/// struct Health(f32);
|
|
///
|
|
/// #[derive(Component)]
|
|
/// struct Buff(f32);
|
|
///
|
|
/// #[derive(WorldQuery)]
|
|
/// #[world_query(mutable)]
|
|
/// struct HealthQuery {
|
|
/// health: &'static mut Health,
|
|
/// buff: Option<&'static mut Buff>,
|
|
/// }
|
|
///
|
|
/// // `HealthQueryItem` is only available when accessing the query with mutable methods.
|
|
/// impl<'w> HealthQueryItem<'w> {
|
|
/// fn damage(&mut self, value: f32) {
|
|
/// self.health.0 -= value;
|
|
/// }
|
|
///
|
|
/// fn total(&self) -> f32 {
|
|
/// self.health.0 + self.buff.as_deref().map_or(0.0, |Buff(buff)| *buff)
|
|
/// }
|
|
/// }
|
|
///
|
|
/// // `HealthQueryReadOnlyItem` is only available when accessing the query with immutable methods.
|
|
/// impl<'w> HealthQueryReadOnlyItem<'w> {
|
|
/// fn total(&self) -> f32 {
|
|
/// self.health.0 + self.buff.map_or(0.0, |Buff(buff)| *buff)
|
|
/// }
|
|
/// }
|
|
///
|
|
/// fn my_system(mut health_query: Query<HealthQuery>) {
|
|
/// // The item returned by the iterator is of type `HealthQueryReadOnlyItem`.
|
|
/// for health in health_query.iter() {
|
|
/// println!("Total: {}", health.total());
|
|
/// }
|
|
/// // The item returned by the iterator is of type `HealthQueryItem`.
|
|
/// for mut health in &mut health_query {
|
|
/// health.damage(1.0);
|
|
/// println!("Total (mut): {}", health.total());
|
|
/// }
|
|
/// }
|
|
/// # bevy_ecs::system::assert_is_system(my_system);
|
|
/// ```
|
|
///
|
|
/// ## Deriving traits for query items
|
|
///
|
|
/// The `WorldQuery` derive macro does not automatically implement the traits of the struct to the query item types.
|
|
/// Something similar can be done by using the `#[world_query(derive(...))]` attribute.
|
|
/// This will apply the listed derivable traits to the query item structs.
|
|
///
|
|
/// ```
|
|
/// # use bevy_ecs::prelude::*;
|
|
/// # use bevy_ecs::query::WorldQuery;
|
|
/// #
|
|
/// # #[derive(Component, Debug)]
|
|
/// # struct ComponentA;
|
|
/// #
|
|
/// #[derive(WorldQuery)]
|
|
/// #[world_query(mutable, derive(Debug))]
|
|
/// struct CustomQuery {
|
|
/// component_a: &'static ComponentA,
|
|
/// }
|
|
///
|
|
/// // This function statically checks that `T` implements `Debug`.
|
|
/// fn assert_debug<T: std::fmt::Debug>() {}
|
|
///
|
|
/// assert_debug::<CustomQueryItem>();
|
|
/// assert_debug::<CustomQueryReadOnlyItem>();
|
|
/// ```
|
|
///
|
|
/// ## Query composition
|
|
///
|
|
/// It is possible to use any `WorldQuery` as a field of another one.
|
|
/// This means that a `WorldQuery` can also be used as a subquery, potentially in multiple places.
|
|
///
|
|
/// ```
|
|
/// # use bevy_ecs::prelude::*;
|
|
/// # use bevy_ecs::query::WorldQuery;
|
|
/// #
|
|
/// # #[derive(Component)]
|
|
/// # struct ComponentA;
|
|
/// # #[derive(Component)]
|
|
/// # struct ComponentB;
|
|
/// # #[derive(Component)]
|
|
/// # struct ComponentC;
|
|
/// #
|
|
/// #[derive(WorldQuery)]
|
|
/// struct SubQuery {
|
|
/// component_a: &'static ComponentA,
|
|
/// component_b: &'static ComponentB,
|
|
/// }
|
|
///
|
|
/// #[derive(WorldQuery)]
|
|
/// struct MyQuery {
|
|
/// subquery: SubQuery,
|
|
/// component_c: &'static ComponentC,
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// ## Filters
|
|
///
|
|
/// Since the query filter type parameter is `WorldQuery`, it is also possible to use this macro to create filters.
|
|
///
|
|
/// ```
|
|
/// # use bevy_ecs::prelude::*;
|
|
/// # use bevy_ecs::{query::WorldQuery, component::Component};
|
|
/// #
|
|
/// # #[derive(Component)]
|
|
/// # struct ComponentA;
|
|
/// # #[derive(Component)]
|
|
/// # struct ComponentB;
|
|
/// # #[derive(Component)]
|
|
/// # struct ComponentC;
|
|
/// # #[derive(Component)]
|
|
/// # struct ComponentD;
|
|
/// # #[derive(Component)]
|
|
/// # struct ComponentE;
|
|
/// #
|
|
/// #[derive(WorldQuery)]
|
|
/// struct MyFilter<T: Component, P: Component> {
|
|
/// // Field names are not relevant, since they are never manually accessed.
|
|
/// with_a: With<ComponentA>,
|
|
/// or_filter: Or<(With<ComponentC>, Added<ComponentB>)>,
|
|
/// generic_tuple: (With<T>, Without<P>),
|
|
/// }
|
|
///
|
|
/// fn my_system(query: Query<Entity, MyFilter<ComponentD, ComponentE>>) {
|
|
/// // ...
|
|
/// }
|
|
/// # bevy_ecs::system::assert_is_system(my_system);
|
|
/// ```
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// Component access of `Self::ReadOnly` must be a subset of `Self`
|
|
/// and `Self::ReadOnly` must match exactly the same archetypes/tables as `Self`
|
|
///
|
|
/// Implementor must ensure that
|
|
/// [`update_component_access`] and [`update_archetype_component_access`]
|
|
/// exactly reflects the results of the following methods:
|
|
///
|
|
/// - [`matches_component_set`]
|
|
/// - [`fetch`]
|
|
///
|
|
/// [`Added`]: crate::query::Added
|
|
/// [`fetch`]: Self::fetch
|
|
/// [`Changed`]: crate::query::Changed
|
|
/// [`Fetch`]: crate::query::WorldQuery::Fetch
|
|
/// [`matches_component_set`]: Self::matches_component_set
|
|
/// [`Or`]: crate::query::Or
|
|
/// [`Query`]: crate::system::Query
|
|
/// [`ReadOnly`]: Self::ReadOnly
|
|
/// [`State`]: Self::State
|
|
/// [`update_archetype_component_access`]: Self::update_archetype_component_access
|
|
/// [`update_component_access`]: Self::update_component_access
|
|
/// [`With`]: crate::query::With
|
|
/// [`Without`]: crate::query::Without
|
|
pub unsafe trait WorldQuery {
|
|
/// The item returned by this [`WorldQuery`]
|
|
type Item<'a>;
|
|
|
|
/// Per archetype/table state used by this [`WorldQuery`] to fetch [`Self::Item`](crate::query::WorldQuery::Item)
|
|
type Fetch<'a>;
|
|
|
|
/// The read-only variant of this [`WorldQuery`], which satisfies the [`ReadOnlyWorldQuery`] trait.
|
|
type ReadOnly: ReadOnlyWorldQuery<State = Self::State>;
|
|
|
|
/// State used to construct a [`Self::Fetch`](crate::query::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
|
|
/// constructing [`Self::Fetch`](crate::query::WorldQuery::Fetch).
|
|
type State: Send + Sync + Sized;
|
|
|
|
/// This function manually implements subtyping for the query items.
|
|
fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort>;
|
|
|
|
/// Creates a new instance of this fetch.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// `state` must have been initialized (via [`WorldQuery::init_state`]) using the same `world` passed
|
|
/// in to this function.
|
|
unsafe fn init_fetch<'w>(
|
|
world: &'w World,
|
|
state: &Self::State,
|
|
last_change_tick: u32,
|
|
change_tick: u32,
|
|
) -> Self::Fetch<'w>;
|
|
|
|
/// While this function can be called for any query, it is always safe to call if `Self: ReadOnlyWorldQuery` holds.
|
|
///
|
|
/// # Safety
|
|
/// While calling this method on its own cannot cause UB it is marked `unsafe` as the caller must ensure
|
|
/// that the returned value is not used in any way that would cause two `QueryItem<Self>` for the same
|
|
/// `archetype_index` or `table_row` to be alive at the same time.
|
|
unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w>;
|
|
|
|
/// Returns true if (and only if) every table of every archetype matched by this fetch contains
|
|
/// all of the matched components. This is used to select a more efficient "table iterator"
|
|
/// for "dense" queries. If this returns true, [`WorldQuery::set_table`] must be used before
|
|
/// [`WorldQuery::fetch`] can be called for iterators. If this returns false,
|
|
/// [`WorldQuery::set_archetype`] must be used before [`WorldQuery::fetch`] can be called for
|
|
/// iterators.
|
|
const IS_DENSE: bool;
|
|
|
|
/// Returns true if (and only if) this Fetch relies strictly on archetypes to limit which
|
|
/// components are accessed by the Query.
|
|
///
|
|
/// This enables optimizations for [`crate::query::QueryIter`] that rely on knowing exactly how
|
|
/// many elements are being iterated (such as `Iterator::collect()`).
|
|
const IS_ARCHETYPAL: bool;
|
|
|
|
/// Adjusts internal state to account for the next [`Archetype`]. This will always be called on
|
|
/// archetypes that match this [`WorldQuery`].
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// `archetype` and `tables` must be from the [`World`] [`WorldQuery::init_state`] was called on. `state` must
|
|
/// be the [`Self::State`] this was initialized with.
|
|
unsafe fn set_archetype<'w>(
|
|
fetch: &mut Self::Fetch<'w>,
|
|
state: &Self::State,
|
|
archetype: &'w Archetype,
|
|
table: &'w Table,
|
|
);
|
|
|
|
/// Adjusts internal state to account for the next [`Table`]. This will always be called on tables
|
|
/// that match this [`WorldQuery`].
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// `table` must be from the [`World`] [`WorldQuery::init_state`] was called on. `state` must be the
|
|
/// [`Self::State`] this was initialized with.
|
|
unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table);
|
|
|
|
/// Fetch [`Self::Item`](`WorldQuery::Item`) for either the given `entity` in the current [`Table`],
|
|
/// or for the given `entity` in the current [`Archetype`]. This must always be called after
|
|
/// [`WorldQuery::set_table`] with a `table_row` in the range of the current [`Table`] or after
|
|
/// [`WorldQuery::set_archetype`] with a `entity` in the current archetype.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// 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 fetch<'w>(
|
|
fetch: &mut Self::Fetch<'w>,
|
|
entity: Entity,
|
|
table_row: usize,
|
|
) -> Self::Item<'w>;
|
|
|
|
/// # Safety
|
|
///
|
|
/// 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.
|
|
#[allow(unused_variables)]
|
|
#[inline(always)]
|
|
unsafe fn filter_fetch(fetch: &mut Self::Fetch<'_>, entity: Entity, table_row: usize) -> bool {
|
|
true
|
|
}
|
|
|
|
// This does not have a default body of `{}` because 99% of cases need to add accesses
|
|
// and forgetting to do so would be unsound.
|
|
fn update_component_access(state: &Self::State, access: &mut FilteredAccess<ComponentId>);
|
|
// This does not have a default body of `{}` becaues 99% of cases need to add accesses
|
|
// and forgetting to do so would be unsound.
|
|
fn update_archetype_component_access(
|
|
state: &Self::State,
|
|
archetype: &Archetype,
|
|
access: &mut Access<ArchetypeComponentId>,
|
|
);
|
|
|
|
fn init_state(world: &mut World) -> Self::State;
|
|
fn matches_component_set(
|
|
state: &Self::State,
|
|
set_contains_id: &impl Fn(ComponentId) -> bool,
|
|
) -> bool;
|
|
}
|
|
|
|
/// A world query that is read only.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// This must only be implemented for read-only [`WorldQuery`]'s.
|
|
pub unsafe trait ReadOnlyWorldQuery: WorldQuery<ReadOnly = Self> {}
|
|
|
|
/// The `Fetch` of a [`WorldQuery`], which is used to store state for each archetype/table.
|
|
pub type QueryFetch<'w, Q> = <Q as WorldQuery>::Fetch<'w>;
|
|
/// The item type returned when a [`WorldQuery`] is iterated over
|
|
pub type QueryItem<'w, Q> = <Q as WorldQuery>::Item<'w>;
|
|
/// The read-only `Fetch` of a [`WorldQuery`], which is used to store state for each archetype/table.
|
|
pub type ROQueryFetch<'w, Q> = QueryFetch<'w, <Q as WorldQuery>::ReadOnly>;
|
|
/// The read-only variant of the item type returned when a [`WorldQuery`] is iterated over immutably
|
|
pub type ROQueryItem<'w, Q> = QueryItem<'w, <Q as WorldQuery>::ReadOnly>;
|
|
|
|
/// SAFETY: no component or archetype access
|
|
unsafe impl WorldQuery for Entity {
|
|
type Fetch<'w> = ();
|
|
type Item<'w> = Entity;
|
|
type ReadOnly = Self;
|
|
type State = ();
|
|
|
|
fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> {
|
|
item
|
|
}
|
|
|
|
const IS_DENSE: bool = true;
|
|
|
|
const IS_ARCHETYPAL: bool = true;
|
|
|
|
unsafe fn init_fetch<'w>(
|
|
_world: &'w World,
|
|
_state: &Self::State,
|
|
_last_change_tick: u32,
|
|
_change_tick: u32,
|
|
) -> Self::Fetch<'w> {
|
|
}
|
|
|
|
unsafe fn clone_fetch<'w>(_fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> {}
|
|
|
|
#[inline]
|
|
unsafe fn set_archetype<'w>(
|
|
_fetch: &mut Self::Fetch<'w>,
|
|
_state: &Self::State,
|
|
_archetype: &'w Archetype,
|
|
_table: &Table,
|
|
) {
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) {
|
|
}
|
|
|
|
#[inline(always)]
|
|
unsafe fn fetch<'w>(
|
|
_fetch: &mut Self::Fetch<'w>,
|
|
entity: Entity,
|
|
_table_row: usize,
|
|
) -> Self::Item<'w> {
|
|
entity
|
|
}
|
|
|
|
fn update_component_access(_state: &Self::State, _access: &mut FilteredAccess<ComponentId>) {}
|
|
|
|
fn update_archetype_component_access(
|
|
_state: &Self::State,
|
|
_archetype: &Archetype,
|
|
_access: &mut Access<ArchetypeComponentId>,
|
|
) {
|
|
}
|
|
|
|
fn init_state(_world: &mut World) {}
|
|
|
|
fn matches_component_set(
|
|
_state: &Self::State,
|
|
_set_contains_id: &impl Fn(ComponentId) -> bool,
|
|
) -> bool {
|
|
true
|
|
}
|
|
}
|
|
|
|
/// SAFETY: access is read only
|
|
unsafe impl ReadOnlyWorldQuery for Entity {}
|
|
|
|
#[doc(hidden)]
|
|
pub struct ReadFetch<'w, T> {
|
|
// T::Storage = TableStorage
|
|
table_components: Option<ThinSlicePtr<'w, UnsafeCell<T>>>,
|
|
// T::Storage = SparseStorage
|
|
sparse_set: Option<&'w ComponentSparseSet>,
|
|
}
|
|
|
|
/// SAFETY: `Self` is the same as `Self::ReadOnly`
|
|
unsafe impl<T: Component> WorldQuery for &T {
|
|
type Fetch<'w> = ReadFetch<'w, T>;
|
|
type Item<'w> = &'w T;
|
|
type ReadOnly = Self;
|
|
type State = ComponentId;
|
|
|
|
fn shrink<'wlong: 'wshort, 'wshort>(item: &'wlong T) -> &'wshort T {
|
|
item
|
|
}
|
|
|
|
const IS_DENSE: bool = {
|
|
match T::Storage::STORAGE_TYPE {
|
|
StorageType::Table => true,
|
|
StorageType::SparseSet => false,
|
|
}
|
|
};
|
|
|
|
const IS_ARCHETYPAL: bool = true;
|
|
|
|
unsafe fn init_fetch<'w>(
|
|
world: &'w World,
|
|
&component_id: &ComponentId,
|
|
_last_change_tick: u32,
|
|
_change_tick: u32,
|
|
) -> ReadFetch<'w, T> {
|
|
ReadFetch {
|
|
table_components: None,
|
|
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| {
|
|
world
|
|
.storages()
|
|
.sparse_sets
|
|
.get(component_id)
|
|
.debug_checked_unwrap()
|
|
}),
|
|
}
|
|
}
|
|
|
|
unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> {
|
|
ReadFetch {
|
|
table_components: fetch.table_components,
|
|
sparse_set: fetch.sparse_set,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn set_archetype<'w>(
|
|
fetch: &mut ReadFetch<'w, T>,
|
|
component_id: &ComponentId,
|
|
_archetype: &'w Archetype,
|
|
table: &'w Table,
|
|
) {
|
|
if Self::IS_DENSE {
|
|
Self::set_table(fetch, component_id, table);
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn set_table<'w>(
|
|
fetch: &mut ReadFetch<'w, T>,
|
|
&component_id: &ComponentId,
|
|
table: &'w Table,
|
|
) {
|
|
fetch.table_components = Some(
|
|
table
|
|
.get_column(component_id)
|
|
.debug_checked_unwrap()
|
|
.get_data_slice()
|
|
.into(),
|
|
);
|
|
}
|
|
|
|
#[inline(always)]
|
|
unsafe fn fetch<'w>(
|
|
fetch: &mut Self::Fetch<'w>,
|
|
entity: Entity,
|
|
table_row: usize,
|
|
) -> Self::Item<'w> {
|
|
match T::Storage::STORAGE_TYPE {
|
|
StorageType::Table => fetch
|
|
.table_components
|
|
.debug_checked_unwrap()
|
|
.get(table_row)
|
|
.deref(),
|
|
StorageType::SparseSet => fetch
|
|
.sparse_set
|
|
.debug_checked_unwrap()
|
|
.get(entity)
|
|
.debug_checked_unwrap()
|
|
.deref(),
|
|
}
|
|
}
|
|
|
|
fn update_component_access(
|
|
&component_id: &ComponentId,
|
|
access: &mut FilteredAccess<ComponentId>,
|
|
) {
|
|
assert!(
|
|
!access.access().has_write(component_id),
|
|
"&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",
|
|
std::any::type_name::<T>(),
|
|
);
|
|
access.add_read(component_id);
|
|
}
|
|
|
|
fn update_archetype_component_access(
|
|
&component_id: &ComponentId,
|
|
archetype: &Archetype,
|
|
access: &mut Access<ArchetypeComponentId>,
|
|
) {
|
|
if let Some(archetype_component_id) = archetype.get_archetype_component_id(component_id) {
|
|
access.add_read(archetype_component_id);
|
|
}
|
|
}
|
|
|
|
fn init_state(world: &mut World) -> ComponentId {
|
|
world.init_component::<T>()
|
|
}
|
|
|
|
fn matches_component_set(
|
|
&state: &ComponentId,
|
|
set_contains_id: &impl Fn(ComponentId) -> bool,
|
|
) -> bool {
|
|
set_contains_id(state)
|
|
}
|
|
}
|
|
|
|
/// SAFETY: access is read only
|
|
unsafe impl<T: Component> ReadOnlyWorldQuery for &T {}
|
|
|
|
#[doc(hidden)]
|
|
pub struct WriteFetch<'w, T> {
|
|
// T::Storage = TableStorage
|
|
table_data: Option<(
|
|
ThinSlicePtr<'w, UnsafeCell<T>>,
|
|
ThinSlicePtr<'w, UnsafeCell<ComponentTicks>>,
|
|
)>,
|
|
// T::Storage = SparseStorage
|
|
sparse_set: Option<&'w ComponentSparseSet>,
|
|
|
|
last_change_tick: u32,
|
|
change_tick: u32,
|
|
}
|
|
|
|
/// SAFETY: access of `&T` is a subset of `&mut T`
|
|
unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
|
|
type Fetch<'w> = WriteFetch<'w, T>;
|
|
type Item<'w> = Mut<'w, T>;
|
|
type ReadOnly = &'__w T;
|
|
type State = ComponentId;
|
|
|
|
fn shrink<'wlong: 'wshort, 'wshort>(item: Mut<'wlong, T>) -> Mut<'wshort, T> {
|
|
item
|
|
}
|
|
|
|
const IS_DENSE: bool = {
|
|
match T::Storage::STORAGE_TYPE {
|
|
StorageType::Table => true,
|
|
StorageType::SparseSet => false,
|
|
}
|
|
};
|
|
|
|
const IS_ARCHETYPAL: bool = true;
|
|
|
|
unsafe fn init_fetch<'w>(
|
|
world: &'w World,
|
|
&component_id: &ComponentId,
|
|
last_change_tick: u32,
|
|
change_tick: u32,
|
|
) -> WriteFetch<'w, T> {
|
|
WriteFetch {
|
|
table_data: None,
|
|
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| {
|
|
world
|
|
.storages()
|
|
.sparse_sets
|
|
.get(component_id)
|
|
.debug_checked_unwrap()
|
|
}),
|
|
last_change_tick,
|
|
change_tick,
|
|
}
|
|
}
|
|
|
|
unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> {
|
|
WriteFetch {
|
|
table_data: fetch.table_data,
|
|
sparse_set: fetch.sparse_set,
|
|
last_change_tick: fetch.last_change_tick,
|
|
change_tick: fetch.change_tick,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn set_archetype<'w>(
|
|
fetch: &mut WriteFetch<'w, T>,
|
|
component_id: &ComponentId,
|
|
_archetype: &'w Archetype,
|
|
table: &'w Table,
|
|
) {
|
|
if Self::IS_DENSE {
|
|
Self::set_table(fetch, component_id, table);
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn set_table<'w>(
|
|
fetch: &mut WriteFetch<'w, T>,
|
|
&component_id: &ComponentId,
|
|
table: &'w Table,
|
|
) {
|
|
let column = table.get_column(component_id).debug_checked_unwrap();
|
|
fetch.table_data = Some((
|
|
column.get_data_slice().into(),
|
|
column.get_ticks_slice().into(),
|
|
));
|
|
}
|
|
|
|
#[inline(always)]
|
|
unsafe fn fetch<'w>(
|
|
fetch: &mut Self::Fetch<'w>,
|
|
entity: Entity,
|
|
table_row: usize,
|
|
) -> Self::Item<'w> {
|
|
match T::Storage::STORAGE_TYPE {
|
|
StorageType::Table => {
|
|
let (table_components, table_ticks) = fetch.table_data.debug_checked_unwrap();
|
|
Mut {
|
|
value: table_components.get(table_row).deref_mut(),
|
|
ticks: Ticks {
|
|
component_ticks: table_ticks.get(table_row).deref_mut(),
|
|
change_tick: fetch.change_tick,
|
|
last_change_tick: fetch.last_change_tick,
|
|
},
|
|
}
|
|
}
|
|
StorageType::SparseSet => {
|
|
let (component, component_ticks) = fetch
|
|
.sparse_set
|
|
.debug_checked_unwrap()
|
|
.get_with_ticks(entity)
|
|
.debug_checked_unwrap();
|
|
Mut {
|
|
value: component.assert_unique().deref_mut(),
|
|
ticks: Ticks {
|
|
component_ticks: component_ticks.deref_mut(),
|
|
change_tick: fetch.change_tick,
|
|
last_change_tick: fetch.last_change_tick,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn update_component_access(
|
|
&component_id: &ComponentId,
|
|
access: &mut FilteredAccess<ComponentId>,
|
|
) {
|
|
assert!(
|
|
!access.access().has_read(component_id),
|
|
"&mut {} conflicts with a previous access in this query. Mutable component access must be unique.",
|
|
std::any::type_name::<T>(),
|
|
);
|
|
access.add_write(component_id);
|
|
}
|
|
|
|
fn update_archetype_component_access(
|
|
&component_id: &ComponentId,
|
|
archetype: &Archetype,
|
|
access: &mut Access<ArchetypeComponentId>,
|
|
) {
|
|
if let Some(archetype_component_id) = archetype.get_archetype_component_id(component_id) {
|
|
access.add_write(archetype_component_id);
|
|
}
|
|
}
|
|
|
|
fn init_state(world: &mut World) -> ComponentId {
|
|
world.init_component::<T>()
|
|
}
|
|
|
|
fn matches_component_set(
|
|
&state: &ComponentId,
|
|
set_contains_id: &impl Fn(ComponentId) -> bool,
|
|
) -> bool {
|
|
set_contains_id(state)
|
|
}
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
pub struct OptionFetch<'w, T: WorldQuery> {
|
|
fetch: T::Fetch<'w>,
|
|
matches: bool,
|
|
}
|
|
|
|
// SAFETY: defers to soundness of `T: WorldQuery` impl
|
|
unsafe impl<T: WorldQuery> WorldQuery for Option<T> {
|
|
type Fetch<'w> = OptionFetch<'w, T>;
|
|
type Item<'w> = Option<T::Item<'w>>;
|
|
type ReadOnly = Option<T::ReadOnly>;
|
|
type State = T::State;
|
|
|
|
fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> {
|
|
item.map(T::shrink)
|
|
}
|
|
|
|
const IS_DENSE: bool = T::IS_DENSE;
|
|
|
|
const IS_ARCHETYPAL: bool = T::IS_ARCHETYPAL;
|
|
|
|
unsafe fn init_fetch<'w>(
|
|
world: &'w World,
|
|
state: &T::State,
|
|
last_change_tick: u32,
|
|
change_tick: u32,
|
|
) -> OptionFetch<'w, T> {
|
|
OptionFetch {
|
|
fetch: T::init_fetch(world, state, last_change_tick, change_tick),
|
|
matches: false,
|
|
}
|
|
}
|
|
|
|
unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> {
|
|
OptionFetch {
|
|
fetch: T::clone_fetch(&fetch.fetch),
|
|
matches: fetch.matches,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn set_archetype<'w>(
|
|
fetch: &mut OptionFetch<'w, T>,
|
|
state: &T::State,
|
|
archetype: &'w Archetype,
|
|
table: &'w Table,
|
|
) {
|
|
fetch.matches = T::matches_component_set(state, &|id| archetype.contains(id));
|
|
if fetch.matches {
|
|
T::set_archetype(&mut fetch.fetch, state, archetype, table);
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn set_table<'w>(fetch: &mut OptionFetch<'w, T>, state: &T::State, table: &'w Table) {
|
|
fetch.matches = T::matches_component_set(state, &|id| table.has_column(id));
|
|
if fetch.matches {
|
|
T::set_table(&mut fetch.fetch, state, table);
|
|
}
|
|
}
|
|
|
|
#[inline(always)]
|
|
unsafe fn fetch<'w>(
|
|
fetch: &mut Self::Fetch<'w>,
|
|
entity: Entity,
|
|
table_row: usize,
|
|
) -> Self::Item<'w> {
|
|
fetch
|
|
.matches
|
|
.then(|| T::fetch(&mut fetch.fetch, entity, table_row))
|
|
}
|
|
|
|
fn update_component_access(state: &T::State, access: &mut FilteredAccess<ComponentId>) {
|
|
// We don't want to add the `with`/`without` of `T` as `Option<T>` will match things regardless of
|
|
// `T`'s filters. for example `Query<(Option<&U>, &mut V)>` will match every entity with a `V` component
|
|
// regardless of whether it has a `U` component. If we dont do this the query will not conflict with
|
|
// `Query<&mut V, Without<U>>` which would be unsound.
|
|
let mut intermediate = access.clone();
|
|
T::update_component_access(state, &mut intermediate);
|
|
access.extend_access(&intermediate);
|
|
}
|
|
|
|
fn update_archetype_component_access(
|
|
state: &T::State,
|
|
archetype: &Archetype,
|
|
access: &mut Access<ArchetypeComponentId>,
|
|
) {
|
|
if T::matches_component_set(state, &|id| archetype.contains(id)) {
|
|
T::update_archetype_component_access(state, archetype, access);
|
|
}
|
|
}
|
|
|
|
fn init_state(world: &mut World) -> T::State {
|
|
T::init_state(world)
|
|
}
|
|
|
|
fn matches_component_set(
|
|
_state: &T::State,
|
|
_set_contains_id: &impl Fn(ComponentId) -> bool,
|
|
) -> bool {
|
|
true
|
|
}
|
|
}
|
|
|
|
/// SAFETY: [`OptionFetch`] is read only because `T` is read only
|
|
unsafe impl<T: ReadOnlyWorldQuery> ReadOnlyWorldQuery for Option<T> {}
|
|
|
|
/// [`WorldQuery`] that tracks changes and additions for component `T`.
|
|
///
|
|
/// Wraps a [`Component`] to track whether the component changed for the corresponding entities in
|
|
/// a query since the last time the system that includes these queries ran.
|
|
///
|
|
/// If you only care about entities that changed or that got added use the
|
|
/// [`Changed`](crate::query::Changed) and [`Added`](crate::query::Added) filters instead.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # use bevy_ecs::component::Component;
|
|
/// # use bevy_ecs::query::ChangeTrackers;
|
|
/// # use bevy_ecs::system::IntoSystem;
|
|
/// # use bevy_ecs::system::Query;
|
|
/// #
|
|
/// # #[derive(Component, Debug)]
|
|
/// # struct Name {};
|
|
/// # #[derive(Component)]
|
|
/// # struct Transform {};
|
|
/// #
|
|
/// fn print_moving_objects_system(query: Query<(&Name, ChangeTrackers<Transform>)>) {
|
|
/// for (name, tracker) in &query {
|
|
/// if tracker.is_changed() {
|
|
/// println!("Entity moved: {:?}", name);
|
|
/// } else {
|
|
/// println!("Entity stood still: {:?}", name);
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// # bevy_ecs::system::assert_is_system(print_moving_objects_system);
|
|
/// ```
|
|
pub struct ChangeTrackers<T: Component> {
|
|
pub(crate) component_ticks: ComponentTicks,
|
|
pub(crate) last_change_tick: u32,
|
|
pub(crate) change_tick: u32,
|
|
marker: PhantomData<T>,
|
|
}
|
|
|
|
impl<T: Component> Clone for ChangeTrackers<T> {
|
|
fn clone(&self) -> Self {
|
|
Self {
|
|
component_ticks: self.component_ticks,
|
|
last_change_tick: self.last_change_tick,
|
|
change_tick: self.change_tick,
|
|
marker: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
impl<T: Component> Copy for ChangeTrackers<T> {}
|
|
|
|
impl<T: Component> std::fmt::Debug for ChangeTrackers<T> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("ChangeTrackers")
|
|
.field("component_ticks", &self.component_ticks)
|
|
.field("last_change_tick", &self.last_change_tick)
|
|
.field("change_tick", &self.change_tick)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl<T: Component> ChangeTrackers<T> {
|
|
/// Returns true if this component has been added since the last execution of this system.
|
|
pub fn is_added(&self) -> bool {
|
|
self.component_ticks
|
|
.is_added(self.last_change_tick, self.change_tick)
|
|
}
|
|
|
|
/// Returns true if this component has been changed since the last execution of this system.
|
|
pub fn is_changed(&self) -> bool {
|
|
self.component_ticks
|
|
.is_changed(self.last_change_tick, self.change_tick)
|
|
}
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
pub struct ChangeTrackersFetch<'w, T> {
|
|
// T::Storage = TableStorage
|
|
table_ticks: Option<ThinSlicePtr<'w, UnsafeCell<ComponentTicks>>>,
|
|
// T::Storage = SparseStorage
|
|
sparse_set: Option<&'w ComponentSparseSet>,
|
|
|
|
marker: PhantomData<T>,
|
|
last_change_tick: u32,
|
|
change_tick: u32,
|
|
}
|
|
|
|
// SAFETY: `ROQueryFetch<Self>` is the same as `QueryFetch<Self>`
|
|
unsafe impl<T: Component> WorldQuery for ChangeTrackers<T> {
|
|
type Fetch<'w> = ChangeTrackersFetch<'w, T>;
|
|
type Item<'w> = ChangeTrackers<T>;
|
|
type ReadOnly = Self;
|
|
type State = ComponentId;
|
|
|
|
fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> {
|
|
item
|
|
}
|
|
|
|
const IS_DENSE: bool = {
|
|
match T::Storage::STORAGE_TYPE {
|
|
StorageType::Table => true,
|
|
StorageType::SparseSet => false,
|
|
}
|
|
};
|
|
|
|
const IS_ARCHETYPAL: bool = true;
|
|
|
|
unsafe fn init_fetch<'w>(
|
|
world: &'w World,
|
|
&component_id: &ComponentId,
|
|
last_change_tick: u32,
|
|
change_tick: u32,
|
|
) -> ChangeTrackersFetch<'w, T> {
|
|
ChangeTrackersFetch {
|
|
table_ticks: None,
|
|
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| {
|
|
world
|
|
.storages()
|
|
.sparse_sets
|
|
.get(component_id)
|
|
.debug_checked_unwrap()
|
|
}),
|
|
marker: PhantomData,
|
|
last_change_tick,
|
|
change_tick,
|
|
}
|
|
}
|
|
|
|
unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> {
|
|
ChangeTrackersFetch {
|
|
table_ticks: fetch.table_ticks,
|
|
sparse_set: fetch.sparse_set,
|
|
marker: fetch.marker,
|
|
last_change_tick: fetch.last_change_tick,
|
|
change_tick: fetch.change_tick,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn set_archetype<'w>(
|
|
fetch: &mut ChangeTrackersFetch<'w, T>,
|
|
component_id: &ComponentId,
|
|
_archetype: &'w Archetype,
|
|
table: &'w Table,
|
|
) {
|
|
if Self::IS_DENSE {
|
|
Self::set_table(fetch, component_id, table);
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn set_table<'w>(
|
|
fetch: &mut ChangeTrackersFetch<'w, T>,
|
|
&id: &ComponentId,
|
|
table: &'w Table,
|
|
) {
|
|
fetch.table_ticks = Some(
|
|
table
|
|
.get_column(id)
|
|
.debug_checked_unwrap()
|
|
.get_ticks_slice()
|
|
.into(),
|
|
);
|
|
}
|
|
|
|
#[inline(always)]
|
|
unsafe fn fetch<'w>(
|
|
fetch: &mut Self::Fetch<'w>,
|
|
entity: Entity,
|
|
table_row: usize,
|
|
) -> Self::Item<'w> {
|
|
match T::Storage::STORAGE_TYPE {
|
|
StorageType::Table => ChangeTrackers {
|
|
component_ticks: {
|
|
let table_ticks = fetch.table_ticks.debug_checked_unwrap();
|
|
table_ticks.get(table_row).read()
|
|
},
|
|
marker: PhantomData,
|
|
last_change_tick: fetch.last_change_tick,
|
|
change_tick: fetch.change_tick,
|
|
},
|
|
StorageType::SparseSet => ChangeTrackers {
|
|
component_ticks: *fetch
|
|
.sparse_set
|
|
.debug_checked_unwrap()
|
|
.get_ticks(entity)
|
|
.debug_checked_unwrap()
|
|
.get(),
|
|
marker: PhantomData,
|
|
last_change_tick: fetch.last_change_tick,
|
|
change_tick: fetch.change_tick,
|
|
},
|
|
}
|
|
}
|
|
|
|
fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) {
|
|
assert!(
|
|
!access.access().has_write(id),
|
|
"ChangeTrackers<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",
|
|
std::any::type_name::<T>()
|
|
);
|
|
access.add_read(id);
|
|
}
|
|
|
|
fn update_archetype_component_access(
|
|
&id: &ComponentId,
|
|
archetype: &Archetype,
|
|
access: &mut Access<ArchetypeComponentId>,
|
|
) {
|
|
if let Some(archetype_component_id) = archetype.get_archetype_component_id(id) {
|
|
access.add_read(archetype_component_id);
|
|
}
|
|
}
|
|
|
|
fn init_state(world: &mut World) -> ComponentId {
|
|
world.init_component::<T>()
|
|
}
|
|
|
|
fn matches_component_set(
|
|
&id: &ComponentId,
|
|
set_contains_id: &impl Fn(ComponentId) -> bool,
|
|
) -> bool {
|
|
set_contains_id(id)
|
|
}
|
|
}
|
|
|
|
/// SAFETY: access is read only
|
|
unsafe impl<T: Component> ReadOnlyWorldQuery for ChangeTrackers<T> {}
|
|
|
|
macro_rules! impl_tuple_fetch {
|
|
($(($name: ident, $state: ident)),*) => {
|
|
#[allow(non_snake_case)]
|
|
#[allow(clippy::unused_unit)]
|
|
// SAFETY: defers to soundness `$name: WorldQuery` impl
|
|
unsafe impl<$($name: WorldQuery),*> WorldQuery for ($($name,)*) {
|
|
type Fetch<'w> = ($($name::Fetch<'w>,)*);
|
|
type Item<'w> = ($($name::Item<'w>,)*);
|
|
type ReadOnly = ($($name::ReadOnly,)*);
|
|
type State = ($($name::State,)*);
|
|
|
|
fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> {
|
|
let ($($name,)*) = item;
|
|
($(
|
|
$name::shrink($name),
|
|
)*)
|
|
}
|
|
|
|
#[allow(clippy::unused_unit)]
|
|
unsafe fn init_fetch<'w>(_world: &'w World, state: &Self::State, _last_change_tick: u32, _change_tick: u32) -> Self::Fetch<'w> {
|
|
let ($($name,)*) = state;
|
|
($($name::init_fetch(_world, $name, _last_change_tick, _change_tick),)*)
|
|
}
|
|
|
|
unsafe fn clone_fetch<'w>(
|
|
fetch: &Self::Fetch<'w>,
|
|
) -> Self::Fetch<'w> {
|
|
let ($($name,)*) = &fetch;
|
|
($($name::clone_fetch($name),)*)
|
|
}
|
|
|
|
const IS_DENSE: bool = true $(&& $name::IS_DENSE)*;
|
|
|
|
const IS_ARCHETYPAL: bool = true $(&& $name::IS_ARCHETYPAL)*;
|
|
|
|
#[inline]
|
|
unsafe fn set_archetype<'w>(
|
|
_fetch: &mut Self::Fetch<'w>,
|
|
_state: &Self::State,
|
|
_archetype: &'w Archetype,
|
|
_table: &'w Table
|
|
) {
|
|
let ($($name,)*) = _fetch;
|
|
let ($($state,)*) = _state;
|
|
$($name::set_archetype($name, $state, _archetype, _table);)*
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) {
|
|
let ($($name,)*) = _fetch;
|
|
let ($($state,)*) = _state;
|
|
$($name::set_table($name, $state, _table);)*
|
|
}
|
|
|
|
#[inline(always)]
|
|
#[allow(clippy::unused_unit)]
|
|
unsafe fn fetch<'w>(
|
|
_fetch: &mut Self::Fetch<'w>,
|
|
_entity: Entity,
|
|
_table_row: usize
|
|
) -> Self::Item<'w> {
|
|
let ($($name,)*) = _fetch;
|
|
($($name::fetch($name, _entity, _table_row),)*)
|
|
}
|
|
|
|
#[inline(always)]
|
|
unsafe fn filter_fetch<'w>(
|
|
_fetch: &mut Self::Fetch<'w>,
|
|
_entity: Entity,
|
|
_table_row: usize
|
|
) -> bool {
|
|
let ($($name,)*) = _fetch;
|
|
true $(&& $name::filter_fetch($name, _entity, _table_row))*
|
|
}
|
|
|
|
fn update_component_access(state: &Self::State, _access: &mut FilteredAccess<ComponentId>) {
|
|
let ($($name,)*) = state;
|
|
$($name::update_component_access($name, _access);)*
|
|
}
|
|
|
|
fn update_archetype_component_access(state: &Self::State, _archetype: &Archetype, _access: &mut Access<ArchetypeComponentId>) {
|
|
let ($($name,)*) = state;
|
|
$($name::update_archetype_component_access($name, _archetype, _access);)*
|
|
}
|
|
|
|
|
|
fn init_state(_world: &mut World) -> Self::State {
|
|
($($name::init_state(_world),)*)
|
|
}
|
|
|
|
fn matches_component_set(state: &Self::State, _set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {
|
|
let ($($name,)*) = state;
|
|
true $(&& $name::matches_component_set($name, _set_contains_id))*
|
|
}
|
|
}
|
|
|
|
/// SAFETY: each item in the tuple is read only
|
|
unsafe impl<$($name: ReadOnlyWorldQuery),*> ReadOnlyWorldQuery for ($($name,)*) {}
|
|
|
|
};
|
|
}
|
|
|
|
/// The `AnyOf` query parameter fetches entities with any of the component types included in T.
|
|
///
|
|
/// `Query<AnyOf<(&A, &B, &mut C)>>` is equivalent to `Query<(Option<&A>, Option<&B>, Option<&mut C>), Or<(With<A>, With<B>, With<C>)>>`.
|
|
/// Each of the components in `T` is returned as an `Option`, as with `Option<A>` queries.
|
|
/// Entities are guaranteed to have at least one of the components in `T`.
|
|
pub struct AnyOf<T>(PhantomData<T>);
|
|
|
|
macro_rules! impl_anytuple_fetch {
|
|
($(($name: ident, $state: ident)),*) => {
|
|
#[allow(non_snake_case)]
|
|
#[allow(clippy::unused_unit)]
|
|
// SAFETY: defers to soundness of `$name: WorldQuery` impl
|
|
unsafe impl<$($name: WorldQuery),*> WorldQuery for AnyOf<($($name,)*)> {
|
|
type Fetch<'w> = ($(($name::Fetch<'w>, bool),)*);
|
|
type Item<'w> = ($(Option<$name::Item<'w>>,)*);
|
|
type ReadOnly = AnyOf<($($name::ReadOnly,)*)>;
|
|
type State = ($($name::State,)*);
|
|
|
|
fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> {
|
|
let ($($name,)*) = item;
|
|
($(
|
|
$name.map($name::shrink),
|
|
)*)
|
|
}
|
|
|
|
#[allow(clippy::unused_unit)]
|
|
unsafe fn init_fetch<'w>(_world: &'w World, state: &Self::State, _last_change_tick: u32, _change_tick: u32) -> Self::Fetch<'w> {
|
|
let ($($name,)*) = state;
|
|
($(($name::init_fetch(_world, $name, _last_change_tick, _change_tick), false),)*)
|
|
}
|
|
|
|
unsafe fn clone_fetch<'w>(
|
|
fetch: &Self::Fetch<'w>,
|
|
) -> Self::Fetch<'w> {
|
|
let ($($name,)*) = &fetch;
|
|
($(($name::clone_fetch(& $name.0), $name.1),)*)
|
|
}
|
|
|
|
const IS_DENSE: bool = true $(&& $name::IS_DENSE)*;
|
|
|
|
const IS_ARCHETYPAL: bool = true $(&& $name::IS_ARCHETYPAL)*;
|
|
|
|
#[inline]
|
|
unsafe fn set_archetype<'w>(
|
|
_fetch: &mut Self::Fetch<'w>,
|
|
_state: &Self::State,
|
|
_archetype: &'w Archetype,
|
|
_table: &'w Table
|
|
) {
|
|
let ($($name,)*) = _fetch;
|
|
let ($($state,)*) = _state;
|
|
$(
|
|
$name.1 = $name::matches_component_set($state, &|id| _archetype.contains(id));
|
|
if $name.1 {
|
|
$name::set_archetype(&mut $name.0, $state, _archetype, _table);
|
|
}
|
|
)*
|
|
}
|
|
|
|
#[inline]
|
|
unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) {
|
|
let ($($name,)*) = _fetch;
|
|
let ($($state,)*) = _state;
|
|
$(
|
|
$name.1 = $name::matches_component_set($state, &|id| _table.has_column(id));
|
|
if $name.1 {
|
|
$name::set_table(&mut $name.0, $state, _table);
|
|
}
|
|
)*
|
|
}
|
|
|
|
#[inline(always)]
|
|
#[allow(clippy::unused_unit)]
|
|
unsafe fn fetch<'w>(
|
|
_fetch: &mut Self::Fetch<'w>,
|
|
_entity: Entity,
|
|
_table_row: usize
|
|
) -> Self::Item<'w> {
|
|
let ($($name,)*) = _fetch;
|
|
($(
|
|
$name.1.then(|| $name::fetch(&mut $name.0, _entity, _table_row)),
|
|
)*)
|
|
}
|
|
|
|
fn update_component_access(state: &Self::State, _access: &mut FilteredAccess<ComponentId>) {
|
|
let ($($name,)*) = state;
|
|
|
|
// We do not unconditionally add `$name`'s `with`/`without` accesses to `_access`
|
|
// as this would be unsound. For example the following two queries should conflict:
|
|
// - Query<(AnyOf<(&A, ())>, &mut B)>
|
|
// - Query<&mut B, Without<A>>
|
|
//
|
|
// If we were to unconditionally add `$name`'s `with`/`without` accesses then `AnyOf<(&A, ())>`
|
|
// would have a `With<A>` access which is incorrect as this `WorldQuery` will match entities that
|
|
// do not have the `A` component. This is the same logic as the `Or<...>: WorldQuery` impl.
|
|
//
|
|
// The correct thing to do here is to only add a `with`/`without` access to `_access` if all
|
|
// `$name` params have that `with`/`without` access. More jargony put- we add the intersection
|
|
// of all `with`/`without` accesses of the `$name` params to `_access`.
|
|
let mut _intersected_access = _access.clone();
|
|
let mut _not_first = false;
|
|
$(
|
|
if _not_first {
|
|
let mut intermediate = _access.clone();
|
|
$name::update_component_access($name, &mut intermediate);
|
|
_intersected_access.extend_intersect_filter(&intermediate);
|
|
_intersected_access.extend_access(&intermediate);
|
|
} else {
|
|
|
|
$name::update_component_access($name, &mut _intersected_access);
|
|
_not_first = true;
|
|
}
|
|
)*
|
|
|
|
*_access = _intersected_access;
|
|
}
|
|
|
|
fn update_archetype_component_access(state: &Self::State, _archetype: &Archetype, _access: &mut Access<ArchetypeComponentId>) {
|
|
let ($($name,)*) = state;
|
|
$(
|
|
if $name::matches_component_set($name, &|id| _archetype.contains(id)) {
|
|
$name::update_archetype_component_access($name, _archetype, _access);
|
|
}
|
|
)*
|
|
}
|
|
|
|
fn init_state(_world: &mut World) -> Self::State {
|
|
($($name::init_state(_world),)*)
|
|
}
|
|
|
|
fn matches_component_set(_state: &Self::State, _set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {
|
|
let ($($name,)*) = _state;
|
|
false $(|| $name::matches_component_set($name, _set_contains_id))*
|
|
}
|
|
}
|
|
|
|
/// SAFETY: each item in the tuple is read only
|
|
unsafe impl<$($name: ReadOnlyWorldQuery),*> ReadOnlyWorldQuery for AnyOf<($($name,)*)> {}
|
|
|
|
};
|
|
}
|
|
|
|
all_tuples!(impl_tuple_fetch, 0, 15, F, S);
|
|
all_tuples!(impl_anytuple_fetch, 0, 15, F, S);
|
|
|
|
/// [`WorldQuery`] used to nullify queries by turning `Query<Q>` into `Query<NopWorldQuery<Q>>`
|
|
///
|
|
/// This will rarely be useful to consumers of `bevy_ecs`.
|
|
pub struct NopWorldQuery<Q: WorldQuery>(PhantomData<Q>);
|
|
|
|
/// SAFETY: `Self::ReadOnly` is `Self`
|
|
unsafe impl<Q: WorldQuery> WorldQuery for NopWorldQuery<Q> {
|
|
type Fetch<'w> = ();
|
|
type Item<'w> = ();
|
|
type ReadOnly = Self;
|
|
type State = Q::State;
|
|
|
|
fn shrink<'wlong: 'wshort, 'wshort>(_: ()) {}
|
|
|
|
const IS_DENSE: bool = Q::IS_DENSE;
|
|
|
|
const IS_ARCHETYPAL: bool = true;
|
|
|
|
#[inline(always)]
|
|
unsafe fn init_fetch(
|
|
_world: &World,
|
|
_state: &Q::State,
|
|
_last_change_tick: u32,
|
|
_change_tick: u32,
|
|
) {
|
|
}
|
|
|
|
unsafe fn clone_fetch<'w>(_fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> {}
|
|
|
|
#[inline(always)]
|
|
unsafe fn set_archetype(
|
|
_fetch: &mut (),
|
|
_state: &Q::State,
|
|
_archetype: &Archetype,
|
|
_tables: &Table,
|
|
) {
|
|
}
|
|
|
|
#[inline(always)]
|
|
unsafe fn set_table<'w>(_fetch: &mut (), _state: &Q::State, _table: &Table) {}
|
|
|
|
#[inline(always)]
|
|
unsafe fn fetch<'w>(
|
|
_fetch: &mut Self::Fetch<'w>,
|
|
_entity: Entity,
|
|
_table_row: usize,
|
|
) -> Self::Item<'w> {
|
|
}
|
|
|
|
fn update_component_access(_state: &Q::State, _access: &mut FilteredAccess<ComponentId>) {}
|
|
|
|
fn update_archetype_component_access(
|
|
_state: &Q::State,
|
|
_archetype: &Archetype,
|
|
_access: &mut Access<ArchetypeComponentId>,
|
|
) {
|
|
}
|
|
|
|
fn init_state(world: &mut World) -> Self::State {
|
|
Q::init_state(world)
|
|
}
|
|
|
|
fn matches_component_set(
|
|
state: &Self::State,
|
|
set_contains_id: &impl Fn(ComponentId) -> bool,
|
|
) -> bool {
|
|
Q::matches_component_set(state, set_contains_id)
|
|
}
|
|
}
|
|
|
|
/// SAFETY: `NopFetch` never accesses any data
|
|
unsafe impl<Q: WorldQuery> ReadOnlyWorldQuery for NopWorldQuery<Q> {}
|