Skip to content

Commit 07ed1d0

Browse files
FriziTheRawMeatballcart
committed
Implement and require #[derive(Component)] on all component structs (#2254)
This implements the most minimal variant of #1843 - a derive for marker trait. This is a prerequisite to more complicated features like statically defined storage type or opt-out component reflection. In order to make component struct's purpose explicit and avoid misuse, it must be annotated with `#[derive(Component)]` (manual impl is discouraged for compatibility). Right now this is just a marker trait, but in the future it might be expanded. Making this change early allows us to make further changes later without breaking backward compatibility for derive macro users. This already prevents a lot of issues, like using bundles in `insert` calls. Primitive types are no longer valid components as well. This can be easily worked around by adding newtype wrappers and deriving `Component` for them. One funny example of prevented bad code (from our own tests) is when an newtype struct or enum variant is used. Previously, it was possible to write `insert(Newtype)` instead of `insert(Newtype(value))`. That code compiled, because function pointers (in this case newtype struct constructor) implement `Send + Sync + 'static`, so we allowed them to be used as components. This is no longer the case and such invalid code will trigger a compile error. Co-authored-by: = <=> Co-authored-by: TheRawMeatball <[email protected]> Co-authored-by: Carter Anderson <[email protected]>
1 parent 5ba2b9a commit 07ed1d0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+1532
-1085
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ bevy_audio = ["bevy_internal/bevy_audio"]
4646
bevy_dynamic_plugin = ["bevy_internal/bevy_dynamic_plugin"]
4747
bevy_gilrs = ["bevy_internal/bevy_gilrs"]
4848
bevy_gltf = ["bevy_internal/bevy_gltf"]
49-
bevy_wgpu = ["bevy_internal/bevy_wgpu"]
49+
bevy_wgpu = ["bevy_internal/bevy_wgpu"]
5050
bevy_winit = ["bevy_internal/bevy_winit"]
5151

5252
trace_chrome = ["bevy_internal/trace_chrome"]

benches/benches/bevy_ecs/commands.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use bevy::ecs::{
2+
component::Component,
23
entity::Entity,
34
system::{Command, CommandQueue, Commands},
45
world::World,
@@ -18,8 +19,11 @@ criterion_group!(
1819
);
1920
criterion_main!(benches);
2021

22+
#[derive(Component)]
2123
struct A;
24+
#[derive(Component)]
2225
struct B;
26+
#[derive(Component)]
2327
struct C;
2428

2529
fn empty_commands(criterion: &mut Criterion) {
@@ -79,10 +83,10 @@ fn spawn_commands(criterion: &mut Criterion) {
7983
group.finish();
8084
}
8185

82-
#[derive(Default)]
86+
#[derive(Default, Component)]
8387
struct Matrix([[f32; 4]; 4]);
8488

85-
#[derive(Default)]
89+
#[derive(Default, Component)]
8690
struct Vec3([f32; 3]);
8791

8892
fn insert_commands(criterion: &mut Criterion) {
@@ -95,14 +99,16 @@ fn insert_commands(criterion: &mut Criterion) {
9599
let mut world = World::default();
96100
let mut command_queue = CommandQueue::default();
97101
let mut entities = Vec::new();
98-
for i in 0..entity_count {
102+
for _ in 0..entity_count {
99103
entities.push(world.spawn().id());
100104
}
101105

102106
bencher.iter(|| {
103107
let mut commands = Commands::new(&mut command_queue, &world);
104108
for entity in entities.iter() {
105-
commands.entity(*entity).insert_bundle((Matrix::default(), Vec3::default()));
109+
commands
110+
.entity(*entity)
111+
.insert_bundle((Matrix::default(), Vec3::default()));
106112
}
107113
drop(commands);
108114
command_queue.apply(&mut world);
@@ -112,7 +118,7 @@ fn insert_commands(criterion: &mut Criterion) {
112118
let mut world = World::default();
113119
let mut command_queue = CommandQueue::default();
114120
let mut entities = Vec::new();
115-
for i in 0..entity_count {
121+
for _ in 0..entity_count {
116122
entities.push(world.spawn().id());
117123
}
118124

benches/benches/bevy_ecs/stages.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use bevy::ecs::{
2+
component::Component,
23
schedule::{Stage, SystemStage},
3-
system::{IntoSystem, Query},
4+
system::Query,
45
world::World,
56
};
67
use criterion::{criterion_group, criterion_main, Criterion};
@@ -12,10 +13,15 @@ fn run_stage(stage: &mut SystemStage, world: &mut World) {
1213
stage.run(world);
1314
}
1415

16+
#[derive(Component)]
1517
struct A(f32);
18+
#[derive(Component)]
1619
struct B(f32);
20+
#[derive(Component)]
1721
struct C(f32);
22+
#[derive(Component)]
1823
struct D(f32);
24+
#[derive(Component)]
1925
struct E(f32);
2026

2127
const ENTITY_BUNCH: usize = 5000;

benches/benches/bevy_ecs/world_get.rs

Lines changed: 104 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
use bevy::ecs::{
2-
component::{ComponentDescriptor, StorageType},
3-
entity::Entity,
4-
world::World,
5-
};
1+
use bevy::ecs::{component::Component, entity::Entity, world::World};
62
use criterion::{black_box, criterion_group, criterion_main, Criterion};
73

84
criterion_group!(
@@ -15,16 +11,18 @@ criterion_group!(
1511
);
1612
criterion_main!(benches);
1713

18-
struct A(f32);
14+
#[derive(Component, Default)]
15+
#[component(storage = "Table")]
16+
struct Table(f32);
17+
#[derive(Component, Default)]
18+
#[component(storage = "SparseSet")]
19+
struct Sparse(f32);
1920

2021
const RANGE: std::ops::Range<u32> = 5..6;
2122

22-
fn setup(entity_count: u32, storage: StorageType) -> World {
23+
fn setup<T: Component + Default>(entity_count: u32) -> World {
2324
let mut world = World::default();
24-
world
25-
.register_component(ComponentDescriptor::new::<A>(storage))
26-
.unwrap();
27-
world.spawn_batch((0..entity_count).map(|_| (A(0.0),)));
25+
world.spawn_batch((0..entity_count).map(|_| (T::default(),)));
2826
world
2927
}
3028

@@ -35,7 +33,7 @@ fn world_entity(criterion: &mut Criterion) {
3533

3634
for entity_count in RANGE.map(|i| i * 10_000) {
3735
group.bench_function(format!("{}_entities", entity_count), |bencher| {
38-
let world = setup(entity_count, StorageType::Table);
36+
let world = setup::<Table>(entity_count);
3937

4038
bencher.iter(|| {
4139
for i in 0..entity_count {
@@ -55,21 +53,26 @@ fn world_get(criterion: &mut Criterion) {
5553
group.measurement_time(std::time::Duration::from_secs(4));
5654

5755
for entity_count in RANGE.map(|i| i * 10_000) {
58-
for storage in [StorageType::Table, StorageType::SparseSet] {
59-
group.bench_function(
60-
format!("{}_entities_{:?}", entity_count, storage),
61-
|bencher| {
62-
let world = setup(entity_count, storage);
63-
64-
bencher.iter(|| {
65-
for i in 0..entity_count {
66-
let entity = Entity::new(i);
67-
assert!(world.get::<A>(entity).is_some());
68-
}
69-
});
70-
},
71-
);
72-
}
56+
group.bench_function(format!("{}_entities_table", entity_count), |bencher| {
57+
let world = setup::<Table>(entity_count);
58+
59+
bencher.iter(|| {
60+
for i in 0..entity_count {
61+
let entity = Entity::new(i);
62+
assert!(world.get::<Table>(entity).is_some());
63+
}
64+
});
65+
});
66+
group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| {
67+
let world = setup::<Sparse>(entity_count);
68+
69+
bencher.iter(|| {
70+
for i in 0..entity_count {
71+
let entity = Entity::new(i);
72+
assert!(world.get::<Sparse>(entity).is_some());
73+
}
74+
});
75+
});
7376
}
7477

7578
group.finish();
@@ -81,22 +84,28 @@ fn world_query_get(criterion: &mut Criterion) {
8184
group.measurement_time(std::time::Duration::from_secs(4));
8285

8386
for entity_count in RANGE.map(|i| i * 10_000) {
84-
for storage in [StorageType::Table, StorageType::SparseSet] {
85-
group.bench_function(
86-
format!("{}_entities_{:?}", entity_count, storage),
87-
|bencher| {
88-
let mut world = setup(entity_count, storage);
89-
let mut query = world.query::<&A>();
90-
91-
bencher.iter(|| {
92-
for i in 0..entity_count {
93-
let entity = Entity::new(i);
94-
assert!(query.get(&world, entity).is_ok());
95-
}
96-
});
97-
},
98-
);
99-
}
87+
group.bench_function(format!("{}_entities_table", entity_count), |bencher| {
88+
let mut world = setup::<Table>(entity_count);
89+
let mut query = world.query::<&Table>();
90+
91+
bencher.iter(|| {
92+
for i in 0..entity_count {
93+
let entity = Entity::new(i);
94+
assert!(query.get(&world, entity).is_ok());
95+
}
96+
});
97+
});
98+
group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| {
99+
let mut world = setup::<Sparse>(entity_count);
100+
let mut query = world.query::<&Sparse>();
101+
102+
bencher.iter(|| {
103+
for i in 0..entity_count {
104+
let entity = Entity::new(i);
105+
assert!(query.get(&world, entity).is_ok());
106+
}
107+
});
108+
});
100109
}
101110

102111
group.finish();
@@ -108,24 +117,32 @@ fn world_query_iter(criterion: &mut Criterion) {
108117
group.measurement_time(std::time::Duration::from_secs(4));
109118

110119
for entity_count in RANGE.map(|i| i * 10_000) {
111-
for storage in [StorageType::Table, StorageType::SparseSet] {
112-
group.bench_function(
113-
format!("{}_entities_{:?}", entity_count, storage),
114-
|bencher| {
115-
let mut world = setup(entity_count, storage);
116-
let mut query = world.query::<&A>();
117-
118-
bencher.iter(|| {
119-
let mut count = 0;
120-
for comp in query.iter(&world) {
121-
black_box(comp);
122-
count += 1;
123-
}
124-
assert_eq!(black_box(count), entity_count);
125-
});
126-
},
127-
);
128-
}
120+
group.bench_function(format!("{}_entities_table", entity_count), |bencher| {
121+
let mut world = setup::<Table>(entity_count);
122+
let mut query = world.query::<&Table>();
123+
124+
bencher.iter(|| {
125+
let mut count = 0;
126+
for comp in query.iter(&world) {
127+
black_box(comp);
128+
count += 1;
129+
}
130+
assert_eq!(black_box(count), entity_count);
131+
});
132+
});
133+
group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| {
134+
let mut world = setup::<Sparse>(entity_count);
135+
let mut query = world.query::<&Sparse>();
136+
137+
bencher.iter(|| {
138+
let mut count = 0;
139+
for comp in query.iter(&world) {
140+
black_box(comp);
141+
count += 1;
142+
}
143+
assert_eq!(black_box(count), entity_count);
144+
});
145+
});
129146
}
130147

131148
group.finish();
@@ -137,24 +154,32 @@ fn world_query_for_each(criterion: &mut Criterion) {
137154
group.measurement_time(std::time::Duration::from_secs(4));
138155

139156
for entity_count in RANGE.map(|i| i * 10_000) {
140-
for storage in [StorageType::Table, StorageType::SparseSet] {
141-
group.bench_function(
142-
format!("{}_entities_{:?}", entity_count, storage),
143-
|bencher| {
144-
let mut world = setup(entity_count, storage);
145-
let mut query = world.query::<&A>();
146-
147-
bencher.iter(|| {
148-
let mut count = 0;
149-
query.for_each(&world, |comp| {
150-
black_box(comp);
151-
count += 1;
152-
});
153-
assert_eq!(black_box(count), entity_count);
154-
});
155-
},
156-
);
157-
}
157+
group.bench_function(format!("{}_entities_table", entity_count), |bencher| {
158+
let mut world = setup::<Table>(entity_count);
159+
let mut query = world.query::<&Table>();
160+
161+
bencher.iter(|| {
162+
let mut count = 0;
163+
query.for_each(&world, |comp| {
164+
black_box(comp);
165+
count += 1;
166+
});
167+
assert_eq!(black_box(count), entity_count);
168+
});
169+
});
170+
group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| {
171+
let mut world = setup::<Sparse>(entity_count);
172+
let mut query = world.query::<&Sparse>();
173+
174+
bencher.iter(|| {
175+
let mut count = 0;
176+
query.for_each(&world, |comp| {
177+
black_box(comp);
178+
count += 1;
179+
});
180+
assert_eq!(black_box(count), entity_count);
181+
});
182+
});
158183
}
159184

160185
group.finish();

crates/bevy_app/src/app.rs

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
use crate::{CoreStage, Events, Plugin, PluginGroup, PluginGroupBuilder, StartupStage};
22
use bevy_ecs::{
3-
component::{Component, ComponentDescriptor},
43
prelude::{FromWorld, IntoExclusiveSystem},
54
schedule::{
6-
IntoSystemDescriptor, RunOnce, Schedule, Stage, StageLabel, State, SystemSet, SystemStage,
5+
IntoSystemDescriptor, RunOnce, Schedule, Stage, StageLabel, State, StateData, SystemSet,
6+
SystemStage,
77
},
8+
system::Resource,
89
world::World,
910
};
1011
use bevy_utils::tracing::debug;
11-
use std::{fmt::Debug, hash::Hash};
12+
use std::fmt::Debug;
1213

1314
#[cfg(feature = "trace")]
1415
use bevy_utils::tracing::info_span;
@@ -493,7 +494,7 @@ impl App {
493494
/// adding [State::get_driver] to additional stages you need it in.
494495
pub fn add_state<T>(&mut self, initial: T) -> &mut Self
495496
where
496-
T: Component + Debug + Clone + Eq + Hash,
497+
T: StateData,
497498
{
498499
self.add_state_to_stage(CoreStage::Update, initial)
499500
}
@@ -505,7 +506,7 @@ impl App {
505506
/// stages you need it in.
506507
pub fn add_state_to_stage<T>(&mut self, stage: impl StageLabel, initial: T) -> &mut Self
507508
where
508-
T: Component + Debug + Clone + Eq + Hash,
509+
T: StateData,
509510
{
510511
self.insert_resource(State::new(initial))
511512
.add_system_set_to_stage(stage, State::<T>::get_driver())
@@ -582,7 +583,7 @@ impl App {
582583
/// ```
583584
pub fn add_event<T>(&mut self) -> &mut Self
584585
where
585-
T: Component,
586+
T: Resource,
586587
{
587588
self.init_resource::<Events<T>>()
588589
.add_system_to_stage(CoreStage::First, Events::<T>::update_system)
@@ -608,7 +609,7 @@ impl App {
608609
/// ```
609610
pub fn insert_resource<T>(&mut self, resource: T) -> &mut Self
610611
where
611-
T: Component,
612+
T: Resource,
612613
{
613614
self.world.insert_resource(resource);
614615
self
@@ -810,18 +811,6 @@ impl App {
810811
self
811812
}
812813

813-
/// Registers a new component using the given [ComponentDescriptor].
814-
///
815-
/// Components do not need to be manually registered. This just provides a way to
816-
/// override default configuration. Attempting to register a component with a type
817-
/// that has already been used by [World] will result in an error.
818-
///
819-
/// See [World::register_component]
820-
pub fn register_component(&mut self, descriptor: ComponentDescriptor) -> &mut Self {
821-
self.world.register_component(descriptor).unwrap();
822-
self
823-
}
824-
825814
/// Adds the type `T` to the type registry resource.
826815
#[cfg(feature = "bevy_reflect")]
827816
pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {

0 commit comments

Comments
 (0)