Skip to content
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
39bf69f
contiguous query data/filter
Jenya705 Nov 30, 2025
19c7a2a
contiguous iter
Jenya705 Dec 1, 2025
9f391c7
fmt
Jenya705 Dec 1, 2025
564b771
fmt
Jenya705 Dec 1, 2025
667955d
safety comment
Jenya705 Dec 1, 2025
6ad54b0
small fixes
Jenya705 Dec 1, 2025
f3ebbc7
removed contiguous query filter
Jenya705 Dec 1, 2025
d66a0cb
macro
Jenya705 Dec 3, 2025
9e54e50
added example into the table
Jenya705 Dec 3, 2025
c5aa426
example's metadata fix
Jenya705 Dec 3, 2025
cd000c1
example fix
Jenya705 Dec 3, 2025
420dbd5
(Contiguous)QueryData's docs
Jenya705 Dec 3, 2025
2fb9cd3
typo
Jenya705 Dec 3, 2025
59cd6c6
docs fix
Jenya705 Dec 4, 2025
6c359ee
Has contiguous impl
Jenya705 Dec 4, 2025
083f714
thinsliceptr refinements
Jenya705 Dec 4, 2025
3881d63
QueryContiguousIter additions
Jenya705 Dec 4, 2025
7d5ba3d
ThinSlicePtr::cast refinements, test ticks and docs
Jenya705 Dec 9, 2025
8571bd7
typo
Jenya705 Dec 9, 2025
d1d9386
cast refinements
Jenya705 Dec 9, 2025
28d40cc
release notes
Jenya705 Dec 9, 2025
8d64ec4
Merge branch 'main' into main
Jenya705 Dec 9, 2025
f2afb53
format fix
Jenya705 Dec 9, 2025
7477821
typo
Jenya705 Dec 9, 2025
f02d4c9
cast
Jenya705 Dec 10, 2025
0d74581
Owned QueryContiguousIter
Jenya705 Jan 21, 2026
2c6c251
ci fix
Jenya705 Jan 21, 2026
9599bfc
Minor fixes
Jenya705 Jan 21, 2026
6ade0c9
No exactsize
Jenya705 Jan 21, 2026
c9c293d
Contiguous[Ref/Mut]
Jenya705 Jan 22, 2026
495c355
fixes
Jenya705 Jan 22, 2026
7f55e03
debug impls
Jenya705 Jan 22, 2026
888a473
safety comments
Jenya705 Jan 22, 2026
1f765c4
example fix
Jenya705 Jan 22, 2026
bef611e
iterators
Jenya705 Jan 22, 2026
f8286d0
into_iter->iter_mut
Jenya705 Jan 22, 2026
9b14f5f
release note
Jenya705 Jan 22, 2026
250a359
reborrow
Jenya705 Jan 22, 2026
2acfca5
documentation
Jenya705 Jan 23, 2026
42733c5
big->large
Jenya705 Jan 23, 2026
4e1cc10
docs
Jenya705 Jan 23, 2026
6e0441f
docs
Jenya705 Jan 23, 2026
526680b
docs
Jenya705 Jan 23, 2026
31d6f8c
removing unsafe
Jenya705 Jan 23, 2026
ecc13e2
Deref(Mut)
Jenya705 Jan 23, 2026
99d116d
typo
Jenya705 Jan 23, 2026
22612d7
into_inner
Jenya705 Jan 23, 2026
371c52c
ContiguousRef::new
Jenya705 Jan 23, 2026
bddcf57
new ticks methods
Jenya705 Jan 23, 2026
6430029
docs fix
Jenya705 Jan 24, 2026
c4bd5ae
split reverse/bypassing change detection
Jenya705 Jan 24, 2026
6c0c771
warning doc on split
Jenya705 Jan 24, 2026
c8b1548
ContiguousComponentTicks(Mut|Ref) methods
Jenya705 Jan 24, 2026
3ee7dd3
Merge branch 'main' into main
Jenya705 Jan 27, 2026
e941398
Merge branch 'main' into main
Jenya705 Feb 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4901,6 +4901,17 @@ description = "Example Pan-Camera Styled Camera Controller for 2D scenes"
category = "Camera"
wasm = true

[[example]]
name = "contiguous_query"
path = "examples/ecs/contiguous_query.rs"
doc-scrape-examples = true

[package.metadata.example.contiguous_query]
name = "Contiguous Query"
description = "Demonstrates contiguous queries"
category = "ECS (Entity Component System)"
wasm = false

[[example]]
name = "clustered_decal_maps"
path = "examples/3d/clustered_decal_maps.rs"
Expand Down
48 changes: 48 additions & 0 deletions benches/benches/bevy_ecs/iteration/iter_simple_contiguous.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use bevy_ecs::prelude::*;
use glam::*;

#[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) {
let iter = self.1.contiguous_iter_mut(&mut self.0).unwrap();
for (velocity, mut position) in iter {
assert!(velocity.len() == position.data_slice().len());
for (v, p) in velocity.iter().zip(position.data_slice_mut().iter_mut()) {
p.0 += v.0;
}
// to match the iter_simple benchmark
position.mark_all_as_updated();
}
}
}
66 changes: 66 additions & 0 deletions benches/benches/bevy_ecs/iteration/iter_simple_contiguous_avx2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use bevy_ecs::prelude::*;
use glam::*;

#[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 supported() -> bool {
is_x86_feature_detected!("avx2")
}

pub fn new() -> Option<Self> {
if !Self::supported() {
return None;
}

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)>();
Some(Self(world, query))
}

#[inline(never)]
pub fn run(&mut self) {
/// # Safety
/// avx2 must be supported
#[target_feature(enable = "avx2")]
unsafe fn exec(position: &mut [Position], velocity: &[Velocity]) {
assert!(position.len() == velocity.len());
for i in 0..position.len() {
position[i].0 += velocity[i].0;
}
}

let iter = self.1.contiguous_iter_mut(&mut self.0).unwrap();
for (velocity, mut position) in iter {
// SAFETY: checked in new
unsafe {
exec(position.data_slice_mut(), velocity);
}
// to match the iter_simple benchmark
position.mark_all_as_updated();
}
}
}
42 changes: 42 additions & 0 deletions benches/benches/bevy_ecs/iteration/iter_simple_no_detection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use bevy_ecs::prelude::*;
use glam::*;

#[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.bypass_change_detection().0 += velocity.0;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use bevy_ecs::prelude::*;
use glam::*;

#[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) {
let iter = self.1.contiguous_iter_mut(&mut self.0).unwrap();
for (velocity, mut position) in iter {
assert!(velocity.len() == position.data_slice().len());
for (v, p) in velocity.iter().zip(position.data_slice_mut().iter_mut()) {
p.0 += v.0;
}
}
}
}
26 changes: 26 additions & 0 deletions benches/benches/bevy_ecs/iteration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ mod iter_frag_sparse;
mod iter_frag_wide;
mod iter_frag_wide_sparse;
mod iter_simple;
mod iter_simple_contiguous;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod iter_simple_contiguous_avx2;
mod iter_simple_foreach;
mod iter_simple_foreach_hybrid;
mod iter_simple_foreach_sparse_set;
mod iter_simple_foreach_wide;
mod iter_simple_foreach_wide_sparse_set;
mod iter_simple_no_detection;
mod iter_simple_no_detection_contiguous;
mod iter_simple_sparse_set;
mod iter_simple_system;
mod iter_simple_wide;
Expand Down Expand Up @@ -40,6 +45,27 @@ fn iter_simple(c: &mut Criterion) {
let mut bench = iter_simple::Benchmark::new();
b.iter(move || bench.run());
});
group.bench_function("base_contiguous", |b| {
let mut bench = iter_simple_contiguous::Benchmark::new();
b.iter(move || bench.run());
});
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
if iter_simple_contiguous_avx2::Benchmark::supported() {
group.bench_function("base_contiguous_avx2", |b| {
let mut bench = iter_simple_contiguous_avx2::Benchmark::new().unwrap();
b.iter(move || bench.run());
});
}
}
group.bench_function("base_no_detection", |b| {
let mut bench = iter_simple_no_detection::Benchmark::new();
b.iter(move || bench.run());
});
group.bench_function("base_no_detection_contiguous", |b| {
let mut bench = iter_simple_no_detection_contiguous::Benchmark::new();
b.iter(move || bench.run());
});
group.bench_function("wide", |b| {
let mut bench = iter_simple_wide::Benchmark::new();
b.iter(move || bench.run());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ struct MutableUnmarked {

#[derive(QueryData)]
#[query_data(mut)]
//~^ ERROR: invalid attribute, expected `mutable` or `derive`
//~^ ERROR: invalid attribute, expected `mutable`, `derive` or `contiguous`
struct MutableInvalidAttribute {
a: &'static mut Foo,
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: invalid attribute, expected `mutable` or `derive`
error: invalid attribute, expected `mutable`, `derive` or `contiguous`
--> tests/ui/world_query_derive.rs:14:14
|
14 | #[query_data(mut)]
Expand Down
Loading