Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/sync_world.rs
9354 views
1
use bevy_app::Plugin;
2
use bevy_derive::{Deref, DerefMut};
3
use bevy_ecs::{
4
component::Component,
5
entity::{ContainsEntity, Entity, EntityEquivalent, EntityHash},
6
lifecycle::{Add, Remove},
7
observer::On,
8
query::With,
9
reflect::ReflectComponent,
10
resource::Resource,
11
system::{Local, Query, ResMut, SystemState},
12
world::{EntityWorldMut, Mut, World},
13
};
14
use bevy_platform::collections::{HashMap, HashSet};
15
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
16
17
/// A plugin that synchronizes entities with [`SyncToRenderWorld`] between the main world and the render world.
18
///
19
/// All entities with the [`SyncToRenderWorld`] component are kept in sync. It
20
/// is automatically added as a required component by [`ExtractComponentPlugin`]
21
/// and [`SyncComponentPlugin`], so it doesn't need to be added manually when
22
/// spawning or as a required component when either of these plugins are used.
23
///
24
/// # Implementation
25
///
26
/// Bevy's renderer is architected independently from the main app.
27
/// It operates in its own separate ECS [`World`], so the renderer logic can run in parallel with the main world logic.
28
/// This is called "Pipelined Rendering", see [`PipelinedRenderingPlugin`] for more information.
29
///
30
/// [`SyncWorldPlugin`] is the first thing that runs every frame and it maintains an entity-to-entity mapping
31
/// between the main world and the render world.
32
/// It does so by spawning and despawning entities in the render world, to match spawned and despawned entities in the main world.
33
/// The link between synced entities is maintained by the [`RenderEntity`] and [`MainEntity`] components.
34
///
35
/// The [`RenderEntity`] contains the corresponding render world entity of a main world entity, while [`MainEntity`] contains
36
/// the corresponding main world entity of a render world entity.
37
/// For convenience, [`QueryData`](bevy_ecs::query::QueryData) implementations are provided for both components:
38
/// adding [`MainEntity`] to a query (without a `&`) will return the corresponding main world [`Entity`],
39
/// and adding [`RenderEntity`] will return the corresponding render world [`Entity`].
40
/// If you have access to the component itself, the underlying entities can be accessed by calling `.id()`.
41
///
42
/// Synchronization is necessary preparation for extraction ([`ExtractSchedule`](crate::ExtractSchedule)), which copies over component data from the main
43
/// to the render world for these entities.
44
///
45
/// ```text
46
/// |--------------------------------------------------------------------|
47
/// | | | Main world update |
48
/// | sync | extract |---------------------------------------------------|
49
/// | | | Render world update |
50
/// |--------------------------------------------------------------------|
51
/// ```
52
///
53
/// An example for synchronized main entities 1v1 and 18v1
54
///
55
/// ```text
56
/// |---------------------------Main World------------------------------|
57
/// | Entity | Component |
58
/// |-------------------------------------------------------------------|
59
/// | ID: 1v1 | PointLight | RenderEntity(ID: 3V1) | SyncToRenderWorld |
60
/// | ID: 18v1 | PointLight | RenderEntity(ID: 5V1) | SyncToRenderWorld |
61
/// |-------------------------------------------------------------------|
62
///
63
/// |----------Render World-----------|
64
/// | Entity | Component |
65
/// |---------------------------------|
66
/// | ID: 3v1 | MainEntity(ID: 1V1) |
67
/// | ID: 5v1 | MainEntity(ID: 18V1) |
68
/// |---------------------------------|
69
///
70
/// ```
71
///
72
/// Note that this effectively establishes a link between the main world entity and the render world entity.
73
/// Not every entity needs to be synchronized, however; only entities with the [`SyncToRenderWorld`] component are synced.
74
/// Adding [`SyncToRenderWorld`] to a main world component will establish such a link.
75
/// Once a synchronized main entity is despawned, its corresponding render entity will be automatically
76
/// despawned in the next `sync`.
77
///
78
/// The sync step does not copy any of component data between worlds, since its often not necessary to transfer over all
79
/// the components of a main world entity.
80
/// The render world probably cares about a `Position` component, but not a `Velocity` component.
81
/// The extraction happens in its own step, independently from, and after synchronization.
82
///
83
/// Moreover, [`SyncWorldPlugin`] only synchronizes *entities*. [`RenderAsset`](crate::render_asset::RenderAsset)s like meshes and textures are handled
84
/// differently.
85
///
86
/// [`PipelinedRenderingPlugin`]: crate::pipelined_rendering::PipelinedRenderingPlugin
87
/// [`ExtractComponentPlugin`]: crate::extract_component::ExtractComponentPlugin
88
/// [`SyncComponentPlugin`]: crate::sync_component::SyncComponentPlugin
89
#[derive(Default)]
90
pub struct SyncWorldPlugin;
91
92
impl Plugin for SyncWorldPlugin {
93
fn build(&self, app: &mut bevy_app::App) {
94
app.init_resource::<PendingSyncEntity>();
95
app.add_observer(
96
|add: On<Add, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
97
pending.push(EntityRecord::Added(add.entity));
98
},
99
);
100
app.add_observer(
101
|remove: On<Remove, SyncToRenderWorld>,
102
mut pending: ResMut<PendingSyncEntity>,
103
query: Query<&RenderEntity>| {
104
if let Ok(e) = query.get(remove.entity) {
105
pending.push(EntityRecord::Removed(*e));
106
};
107
},
108
);
109
}
110
}
111
/// Marker component that indicates that its entity needs to be synchronized to the render world.
112
///
113
/// This component is automatically added as a required component by [`ExtractComponentPlugin`] and [`SyncComponentPlugin`].
114
/// For more information see [`SyncWorldPlugin`].
115
///
116
/// NOTE: This component should persist throughout the entity's entire lifecycle.
117
/// If this component is removed from its entity, the entity will be despawned.
118
///
119
/// [`ExtractComponentPlugin`]: crate::extract_component::ExtractComponentPlugin
120
/// [`SyncComponentPlugin`]: crate::sync_component::SyncComponentPlugin
121
#[derive(Component, Copy, Clone, Debug, Default, Reflect)]
122
#[reflect(Component, Default, Clone)]
123
#[component(storage = "SparseSet")]
124
pub struct SyncToRenderWorld;
125
126
/// Component added on the main world entities that are synced to the Render World in order to keep track of the corresponding render world entity.
127
///
128
/// Can also be used as a newtype wrapper for render world entities.
129
#[derive(Component, Deref, Copy, Clone, Debug, Eq, Hash, PartialEq, Reflect)]
130
#[component(clone_behavior = Ignore)]
131
#[reflect(Component, Clone)]
132
pub struct RenderEntity(Entity);
133
impl RenderEntity {
134
#[inline]
135
pub fn id(&self) -> Entity {
136
self.0
137
}
138
}
139
140
impl From<Entity> for RenderEntity {
141
fn from(entity: Entity) -> Self {
142
RenderEntity(entity)
143
}
144
}
145
146
impl ContainsEntity for RenderEntity {
147
fn entity(&self) -> Entity {
148
self.id()
149
}
150
}
151
152
// SAFETY: RenderEntity is a newtype around Entity that derives its comparison traits.
153
unsafe impl EntityEquivalent for RenderEntity {}
154
155
/// Component added on the render world entities to keep track of the corresponding main world entity.
156
///
157
/// Can also be used as a newtype wrapper for main world entities.
158
#[derive(Component, Deref, Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Reflect)]
159
#[reflect(Component, Clone)]
160
pub struct MainEntity(Entity);
161
impl MainEntity {
162
#[inline]
163
pub fn id(&self) -> Entity {
164
self.0
165
}
166
}
167
168
impl From<Entity> for MainEntity {
169
fn from(entity: Entity) -> Self {
170
MainEntity(entity)
171
}
172
}
173
174
impl ContainsEntity for MainEntity {
175
fn entity(&self) -> Entity {
176
self.id()
177
}
178
}
179
180
// SAFETY: RenderEntity is a newtype around Entity that derives its comparison traits.
181
unsafe impl EntityEquivalent for MainEntity {}
182
183
/// A [`HashMap`] pre-configured to use [`EntityHash`] hashing with a [`MainEntity`].
184
pub type MainEntityHashMap<V> = HashMap<MainEntity, V, EntityHash>;
185
186
/// A [`HashSet`] pre-configured to use [`EntityHash`] hashing with a [`MainEntity`]..
187
pub type MainEntityHashSet = HashSet<MainEntity, EntityHash>;
188
189
/// Marker component that indicates that its entity needs to be despawned at the end of the frame.
190
#[derive(Component, Copy, Clone, Debug, Default, Reflect)]
191
#[reflect(Component, Default, Clone)]
192
pub struct TemporaryRenderEntity;
193
194
/// A record enum to what entities with [`SyncToRenderWorld`] have been added or removed.
195
#[derive(Debug)]
196
pub(crate) enum EntityRecord {
197
/// When an entity is spawned on the main world, notify the render world so that it can spawn a corresponding
198
/// entity. This contains the main world entity.
199
Added(Entity),
200
/// When an entity is despawned on the main world, notify the render world so that the corresponding entity can be
201
/// despawned. This contains the render world entity.
202
Removed(RenderEntity),
203
/// When a component is removed from an entity, notify the render world so that the corresponding component can be
204
/// removed. This contains the main world entity.
205
ComponentRemoved(Entity, fn(EntityWorldMut<'_>)),
206
}
207
208
// Entity Record in MainWorld pending to Sync
209
#[derive(Resource, Default, Deref, DerefMut)]
210
pub(crate) struct PendingSyncEntity {
211
records: Vec<EntityRecord>,
212
}
213
214
pub(crate) fn entity_sync_system(main_world: &mut World, render_world: &mut World) {
215
main_world.resource_scope(|world, mut pending: Mut<PendingSyncEntity>| {
216
// TODO : batching record
217
for record in pending.drain(..) {
218
match record {
219
EntityRecord::Added(e) => {
220
if let Ok(mut main_entity) = world.get_entity_mut(e) {
221
match main_entity.entry::<RenderEntity>() {
222
bevy_ecs::world::ComponentEntry::Occupied(_) => {
223
panic!("Attempting to synchronize an entity that has already been synchronized!");
224
}
225
bevy_ecs::world::ComponentEntry::Vacant(entry) => {
226
let id = render_world.spawn(MainEntity(e)).id();
227
228
entry.insert(RenderEntity(id));
229
}
230
};
231
}
232
}
233
EntityRecord::Removed(render_entity) => {
234
if let Ok(ec) = render_world.get_entity_mut(render_entity.id()) {
235
ec.despawn();
236
};
237
}
238
EntityRecord::ComponentRemoved(main_entity, removal_function) => {
239
let Some(render_entity) = world.get::<RenderEntity>(main_entity) else {
240
continue;
241
};
242
if let Ok(render_world_entity) = render_world.get_entity_mut(render_entity.id()) {
243
removal_function(render_world_entity);
244
}
245
},
246
}
247
}
248
});
249
}
250
251
pub(crate) fn despawn_temporary_render_entities(
252
world: &mut World,
253
state: &mut SystemState<Query<Entity, With<TemporaryRenderEntity>>>,
254
mut local: Local<Vec<Entity>>,
255
) {
256
let query = state.get(world);
257
258
local.extend(query.iter());
259
260
// Ensure next frame allocation keeps order
261
local.sort_unstable_by_key(|e| e.index());
262
for e in local.drain(..).rev() {
263
world.despawn(e);
264
}
265
}
266
267
/// This module exists to keep the complex unsafe code out of the main module.
268
///
269
/// The implementations for both [`MainEntity`] and [`RenderEntity`] should stay in sync,
270
/// and are based off of the `&T` implementation in `bevy_ecs`.
271
mod render_entities_world_query_impls {
272
use super::{MainEntity, RenderEntity};
273
274
use bevy_ecs::{
275
archetype::Archetype,
276
change_detection::Tick,
277
component::{ComponentId, Components},
278
entity::Entity,
279
query::{
280
ArchetypeQueryData, FilteredAccess, QueryData, ReadOnlyQueryData,
281
ReleaseStateQueryData, WorldQuery,
282
},
283
storage::{Table, TableRow},
284
world::{unsafe_world_cell::UnsafeWorldCell, World},
285
};
286
287
// SAFETY: defers completely to `&RenderEntity` implementation,
288
// and then only modifies the output safely.
289
unsafe impl WorldQuery for RenderEntity {
290
type Fetch<'w> = <&'static RenderEntity as WorldQuery>::Fetch<'w>;
291
type State = <&'static RenderEntity as WorldQuery>::State;
292
293
fn shrink_fetch<'wlong: 'wshort, 'wshort>(
294
fetch: Self::Fetch<'wlong>,
295
) -> Self::Fetch<'wshort> {
296
fetch
297
}
298
299
#[inline]
300
unsafe fn init_fetch<'w, 's>(
301
world: UnsafeWorldCell<'w>,
302
component_id: &'s ComponentId,
303
last_run: Tick,
304
this_run: Tick,
305
) -> Self::Fetch<'w> {
306
// SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
307
unsafe {
308
<&RenderEntity as WorldQuery>::init_fetch(world, component_id, last_run, this_run)
309
}
310
}
311
312
const IS_DENSE: bool = <&'static RenderEntity as WorldQuery>::IS_DENSE;
313
314
#[inline]
315
unsafe fn set_archetype<'w, 's>(
316
fetch: &mut Self::Fetch<'w>,
317
component_id: &'s ComponentId,
318
archetype: &'w Archetype,
319
table: &'w Table,
320
) {
321
// SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
322
unsafe {
323
<&RenderEntity as WorldQuery>::set_archetype(fetch, component_id, archetype, table);
324
}
325
}
326
327
#[inline]
328
unsafe fn set_table<'w, 's>(
329
fetch: &mut Self::Fetch<'w>,
330
&component_id: &'s ComponentId,
331
table: &'w Table,
332
) {
333
// SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
334
unsafe { <&RenderEntity as WorldQuery>::set_table(fetch, &component_id, table) }
335
}
336
337
fn update_component_access(&component_id: &ComponentId, access: &mut FilteredAccess) {
338
<&RenderEntity as WorldQuery>::update_component_access(&component_id, access);
339
}
340
341
fn init_state(world: &mut World) -> ComponentId {
342
<&RenderEntity as WorldQuery>::init_state(world)
343
}
344
345
fn get_state(components: &Components) -> Option<Self::State> {
346
<&RenderEntity as WorldQuery>::get_state(components)
347
}
348
349
fn matches_component_set(
350
&state: &ComponentId,
351
set_contains_id: &impl Fn(ComponentId) -> bool,
352
) -> bool {
353
<&RenderEntity as WorldQuery>::matches_component_set(&state, set_contains_id)
354
}
355
}
356
357
// SAFETY: Component access of Self::ReadOnly is a subset of Self.
358
// Self::ReadOnly matches exactly the same archetypes/tables as Self.
359
unsafe impl QueryData for RenderEntity {
360
const IS_READ_ONLY: bool = true;
361
const IS_ARCHETYPAL: bool = <&MainEntity as QueryData>::IS_ARCHETYPAL;
362
type ReadOnly = RenderEntity;
363
type Item<'w, 's> = Entity;
364
365
fn shrink<'wlong: 'wshort, 'wshort, 's>(
366
item: Self::Item<'wlong, 's>,
367
) -> Self::Item<'wshort, 's> {
368
item
369
}
370
371
#[inline(always)]
372
unsafe fn fetch<'w, 's>(
373
state: &'s Self::State,
374
fetch: &mut Self::Fetch<'w>,
375
entity: Entity,
376
table_row: TableRow,
377
) -> Option<Self::Item<'w, 's>> {
378
// SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
379
let component =
380
unsafe { <&RenderEntity as QueryData>::fetch(state, fetch, entity, table_row) };
381
component.map(RenderEntity::id)
382
}
383
384
fn iter_access(
385
state: &Self::State,
386
) -> impl Iterator<Item = bevy_ecs::query::EcsAccessType<'_>> {
387
<&RenderEntity as QueryData>::iter_access(state)
388
}
389
}
390
391
// SAFETY: the underlying `Entity` is copied, and no mutable access is provided.
392
unsafe impl ReadOnlyQueryData for RenderEntity {}
393
394
impl ArchetypeQueryData for RenderEntity {}
395
396
impl ReleaseStateQueryData for RenderEntity {
397
fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> {
398
item
399
}
400
}
401
402
// SAFETY: defers completely to `&RenderEntity` implementation,
403
// and then only modifies the output safely.
404
unsafe impl WorldQuery for MainEntity {
405
type Fetch<'w> = <&'static MainEntity as WorldQuery>::Fetch<'w>;
406
type State = <&'static MainEntity as WorldQuery>::State;
407
408
fn shrink_fetch<'wlong: 'wshort, 'wshort>(
409
fetch: Self::Fetch<'wlong>,
410
) -> Self::Fetch<'wshort> {
411
fetch
412
}
413
414
#[inline]
415
unsafe fn init_fetch<'w, 's>(
416
world: UnsafeWorldCell<'w>,
417
component_id: &'s ComponentId,
418
last_run: Tick,
419
this_run: Tick,
420
) -> Self::Fetch<'w> {
421
// SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
422
unsafe {
423
<&MainEntity as WorldQuery>::init_fetch(world, component_id, last_run, this_run)
424
}
425
}
426
427
const IS_DENSE: bool = <&'static MainEntity as WorldQuery>::IS_DENSE;
428
429
#[inline]
430
unsafe fn set_archetype<'w, 's>(
431
fetch: &mut Self::Fetch<'w>,
432
component_id: &ComponentId,
433
archetype: &'w Archetype,
434
table: &'w Table,
435
) {
436
// SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
437
unsafe {
438
<&MainEntity as WorldQuery>::set_archetype(fetch, component_id, archetype, table);
439
}
440
}
441
442
#[inline]
443
unsafe fn set_table<'w, 's>(
444
fetch: &mut Self::Fetch<'w>,
445
&component_id: &'s ComponentId,
446
table: &'w Table,
447
) {
448
// SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
449
unsafe { <&MainEntity as WorldQuery>::set_table(fetch, &component_id, table) }
450
}
451
452
fn update_component_access(&component_id: &ComponentId, access: &mut FilteredAccess) {
453
<&MainEntity as WorldQuery>::update_component_access(&component_id, access);
454
}
455
456
fn init_state(world: &mut World) -> ComponentId {
457
<&MainEntity as WorldQuery>::init_state(world)
458
}
459
460
fn get_state(components: &Components) -> Option<Self::State> {
461
<&MainEntity as WorldQuery>::get_state(components)
462
}
463
464
fn matches_component_set(
465
&state: &ComponentId,
466
set_contains_id: &impl Fn(ComponentId) -> bool,
467
) -> bool {
468
<&MainEntity as WorldQuery>::matches_component_set(&state, set_contains_id)
469
}
470
}
471
472
// SAFETY: Component access of Self::ReadOnly is a subset of Self.
473
// Self::ReadOnly matches exactly the same archetypes/tables as Self.
474
unsafe impl QueryData for MainEntity {
475
const IS_READ_ONLY: bool = true;
476
const IS_ARCHETYPAL: bool = <&MainEntity as QueryData>::IS_ARCHETYPAL;
477
type ReadOnly = MainEntity;
478
type Item<'w, 's> = Entity;
479
480
fn shrink<'wlong: 'wshort, 'wshort, 's>(
481
item: Self::Item<'wlong, 's>,
482
) -> Self::Item<'wshort, 's> {
483
item
484
}
485
486
#[inline(always)]
487
unsafe fn fetch<'w, 's>(
488
state: &'s Self::State,
489
fetch: &mut Self::Fetch<'w>,
490
entity: Entity,
491
table_row: TableRow,
492
) -> Option<Self::Item<'w, 's>> {
493
// SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
494
let component =
495
unsafe { <&MainEntity as QueryData>::fetch(state, fetch, entity, table_row) };
496
component.map(MainEntity::id)
497
}
498
499
fn iter_access(
500
state: &Self::State,
501
) -> impl Iterator<Item = bevy_ecs::query::EcsAccessType<'_>> {
502
<&MainEntity as QueryData>::iter_access(state)
503
}
504
}
505
506
// SAFETY: the underlying `Entity` is copied, and no mutable access is provided.
507
unsafe impl ReadOnlyQueryData for MainEntity {}
508
509
impl ArchetypeQueryData for MainEntity {}
510
511
impl ReleaseStateQueryData for MainEntity {
512
fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> {
513
item
514
}
515
}
516
}
517
518
#[cfg(test)]
519
mod tests {
520
use bevy_ecs::{
521
component::Component,
522
entity::Entity,
523
lifecycle::{Add, Remove},
524
observer::On,
525
query::With,
526
system::{Query, ResMut},
527
world::World,
528
};
529
530
use super::{
531
entity_sync_system, EntityRecord, MainEntity, PendingSyncEntity, RenderEntity,
532
SyncToRenderWorld,
533
};
534
535
#[derive(Component)]
536
struct RenderDataComponent;
537
538
#[test]
539
fn sync_world() {
540
let mut main_world = World::new();
541
let mut render_world = World::new();
542
main_world.init_resource::<PendingSyncEntity>();
543
544
main_world.add_observer(
545
|add: On<Add, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
546
pending.push(EntityRecord::Added(add.entity));
547
},
548
);
549
main_world.add_observer(
550
|remove: On<Remove, SyncToRenderWorld>,
551
mut pending: ResMut<PendingSyncEntity>,
552
query: Query<&RenderEntity>| {
553
if let Ok(e) = query.get(remove.entity) {
554
pending.push(EntityRecord::Removed(*e));
555
};
556
},
557
);
558
559
// spawn some empty entities for test
560
for _ in 0..99 {
561
main_world.spawn_empty();
562
}
563
564
// spawn
565
let main_entity = main_world
566
.spawn(RenderDataComponent)
567
// indicates that its entity needs to be synchronized to the render world
568
.insert(SyncToRenderWorld)
569
.id();
570
571
entity_sync_system(&mut main_world, &mut render_world);
572
573
let mut q = render_world.query_filtered::<Entity, With<MainEntity>>();
574
575
// Only one synchronized entity
576
assert!(q.iter(&render_world).count() == 1);
577
578
let render_entity = q.single(&render_world).unwrap();
579
let render_entity_component = main_world.get::<RenderEntity>(main_entity).unwrap();
580
581
assert!(render_entity_component.id() == render_entity);
582
583
let main_entity_component = render_world
584
.get::<MainEntity>(render_entity_component.id())
585
.unwrap();
586
587
assert!(main_entity_component.id() == main_entity);
588
589
// despawn
590
main_world.despawn(main_entity);
591
592
entity_sync_system(&mut main_world, &mut render_world);
593
594
// Only one synchronized entity
595
assert!(q.iter(&render_world).count() == 0);
596
}
597
}
598
599