Skip to content
Open
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4877,3 +4877,14 @@ name = "Pan Camera"
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
47 changes: 47 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,47 @@
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 mut iter = self.1.iter_mut(&mut self.0);
for (velocity, (position, mut ticks)) in iter.as_contiguous_iter().unwrap() {
for (v, p) in velocity.iter().zip(position.iter_mut()) {
p.0 += v.0;
}
// to match the iter_simple benchmark
ticks.mark_all_as_updated();
}
}
}
65 changes: 65 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,65 @@
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")]
fn exec(position: &mut [Position], velocity: &[Velocity]) {
for i in 0..position.len() {
position[i].0 += velocity[i].0;
}
}

let mut iter = self.1.iter_mut(&mut self.0);
for (velocity, (position, mut ticks)) in iter.as_contiguous_iter().unwrap() {
// SAFETY: checked in new
unsafe {
exec(position, velocity);
}
// to match the iter_simple benchmark
ticks.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,45 @@
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 mut iter = self.1.iter_mut(&mut self.0);
for (velocity, (position, _ticks)) in iter.as_contiguous_iter().unwrap() {
for (v, p) in velocity.iter().zip(position.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