# Objective
Fix typos and other small issues in the documentation. I can drop the
changes to `bevy_reflect`'s and `bevy_anti_alias`'s crate descriptions
if it's a problem.
# Objective
Enables accessing slices from tables directly via Queries.
Fixes: #21861
## Solution
One new trait:
- `ContiguousQueryData` allows to fetch all values from tables all at
once (an implementation for `&T` returns a slice of components in the
set table, for `&mut T` returns a mutable slice of components in the set
table as well as a struct with methods to set update ticks (to match the
`fetch` implementation))
Methods `contiguous_iter`, `contiguous_iter_mut` and similar in `Query`
and `QueryState` making possible to iterate using these traits.
Macro `QueryData` was updated to support contiguous items when
`contiguous(target)` attribute is added (a target can be `all`,
`mutable` and `immutable`, refer to the `custom_query_param` example)
## Testing
- `sparse_set_contiguous_query` test verifies that you can't use
`next_contiguous` with sparse set components
- `test_contiguous_query_data` test verifies that returned values are
valid
- `base_contiguous` benchmark (file is named
`iter_simple_contiguous.rs`)
- `base_no_detection` benchmark (file is named
`iter_simple_no_detection.rs`)
- `base_no_detection_contiguous` benchmark (file is named
`iter_simple_no_detection_contiguous.rs`)
- `base_contiguous_avx2` benchmark (file is named
`iter_simple_contiguous_avx2.rs`)
---
## Showcase
Examples `contiguous_query`, `custom_query_param`
### Example
```rust
// - self.0 is a World
// - self.1 is a QueryState
// - velocity is a slice of components with Vec3 inside.
// - position is a data structure which implements Deref/DerefMut and IntoIterator methods to access the slice
// as well as mechanism to update update ticks (which it does automatically on dereference),
// which may be bypassed via `bypass_change_detection` methods.
for (velocity, mut position) in self.1.contiguous_iter_mut(&mut self.0).unwrap() {
assert!(velocity.len() == position.len());
for (v, p) in velocity.iter().zip(position.iter_mut()) {
p.0 += v.0;
}
}
```
### Benchmarks
Code for `base` benchmark:
```rust
#[derive(Component, Copy, Clone)]
struct Transform(Mat4);
#[derive(Component, Copy, Clone)]
struct Position(Vec3);
#[derive(Component, Copy, Clone)]
struct Rotation(Vec3);
#[derive(Component, Copy, Clone)]
struct Velocity(Vec3);
pub struct Benchmark<'w>(World, QueryState<(&'w Velocity, &'w mut Position)>);
impl<'w> Benchmark<'w> {
pub fn new() -> Self {
let mut world = World::new();
world.spawn_batch(core::iter::repeat_n(
(
Transform(Mat4::from_scale(Vec3::ONE)),
Position(Vec3::X),
Rotation(Vec3::X),
Velocity(Vec3::X),
),
10_000,
));
let query = world.query::<(&Velocity, &mut Position)>();
Self(world, query)
}
#[inline(never)]
pub fn run(&mut self) {
for (velocity, mut position) in self.1.iter_mut(&mut self.0) {
position.0 += velocity.0;
}
}
}
```
Iterating over 10000 entities from **a single** table and increasing a
3-dimensional vector from component `Position` by a 3-dimensional vector
from component `Velocity`
| Name | Time | Time (AVX2) | Description |
|------------------------------|-----------|-------------|--------------------------------------------------------------------|
| base | 5.5828 µs | 5.5122 µs | Iteration over components |
| base_contiguous | 4.8825 µs | 1.8665 µs | Iteration over contiguous
chunks |
| base_contiguous_avx2 | 2.0740 µs | 1.8665 µs | Iteration over
contiguous chunks with enforced avx2 optimizations |
| base_no_detection | 4.8065 µs | 4.7723 µs | Iteration over components
while bypassing change detection through `bypass_change_detection()`
method |
| base_no_detection_contiguous | 4.3979 µs | 1.5797 µs | Iteration over
components without registering update ticks |
Using contiguous 'iterator' makes the program a little bit faster and it
can be further vectorized to make it even faster