bevy_render/
sync_world.rs

1use bevy_app::Plugin;
2use bevy_derive::{Deref, DerefMut};
3use 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::{Mut, World},
13};
14use bevy_platform::collections::{HashMap, HashSet};
15use 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)]
90pub struct SyncWorldPlugin;
91
92impl 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")]
124pub 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)]
132pub struct RenderEntity(Entity);
133impl RenderEntity {
134    #[inline]
135    pub fn id(&self) -> Entity {
136        self.0
137    }
138}
139
140impl From<Entity> for RenderEntity {
141    fn from(entity: Entity) -> Self {
142        RenderEntity(entity)
143    }
144}
145
146impl 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.
153unsafe 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)]
160pub struct MainEntity(Entity);
161impl MainEntity {
162    #[inline]
163    pub fn id(&self) -> Entity {
164        self.0
165    }
166}
167
168impl From<Entity> for MainEntity {
169    fn from(entity: Entity) -> Self {
170        MainEntity(entity)
171    }
172}
173
174impl 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.
181unsafe impl EntityEquivalent for MainEntity {}
182
183/// A [`HashMap`] pre-configured to use [`EntityHash`] hashing with a [`MainEntity`].
184pub type MainEntityHashMap<V> = HashMap<MainEntity, V, EntityHash>;
185
186/// A [`HashSet`] pre-configured to use [`EntityHash`] hashing with a [`MainEntity`]..
187pub 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)]
192pub struct TemporaryRenderEntity;
193
194/// A record enum to what entities with [`SyncToRenderWorld`] have been added or removed.
195#[derive(Debug)]
196pub(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),
206}
207
208// Entity Record in MainWorld pending to Sync
209#[derive(Resource, Default, Deref, DerefMut)]
210pub(crate) struct PendingSyncEntity {
211    records: Vec<EntityRecord>,
212}
213
214pub(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) => {
239                    let Some(mut render_entity) = world.get_mut::<RenderEntity>(main_entity) else {
240                        continue;
241                    };
242                    if let Ok(render_world_entity) = render_world.get_entity_mut(render_entity.id()) {
243                        // In order to handle components that extract to derived components, we clear the entity
244                        // and let the extraction system re-add the components.
245                        render_world_entity.despawn();
246
247                        let id = render_world.spawn(MainEntity(main_entity)).id();
248                        render_entity.0 = id;
249                    }
250                },
251            }
252        }
253    });
254}
255
256pub(crate) fn despawn_temporary_render_entities(
257    world: &mut World,
258    state: &mut SystemState<Query<Entity, With<TemporaryRenderEntity>>>,
259    mut local: Local<Vec<Entity>>,
260) {
261    let query = state.get(world);
262
263    local.extend(query.iter());
264
265    // Ensure next frame allocation keeps order
266    local.sort_unstable_by_key(|e| e.index());
267    for e in local.drain(..).rev() {
268        world.despawn(e);
269    }
270}
271
272/// This module exists to keep the complex unsafe code out of the main module.
273///
274/// The implementations for both [`MainEntity`] and [`RenderEntity`] should stay in sync,
275/// and are based off of the `&T` implementation in `bevy_ecs`.
276mod render_entities_world_query_impls {
277    use super::{MainEntity, RenderEntity};
278
279    use bevy_ecs::{
280        archetype::Archetype,
281        change_detection::Tick,
282        component::{ComponentId, Components},
283        entity::Entity,
284        query::{
285            ArchetypeQueryData, FilteredAccess, QueryData, ReadOnlyQueryData,
286            ReleaseStateQueryData, WorldQuery,
287        },
288        storage::{Table, TableRow},
289        world::{unsafe_world_cell::UnsafeWorldCell, World},
290    };
291
292    /// SAFETY: defers completely to `&RenderEntity` implementation,
293    /// and then only modifies the output safely.
294    unsafe impl WorldQuery for RenderEntity {
295        type Fetch<'w> = <&'static RenderEntity as WorldQuery>::Fetch<'w>;
296        type State = <&'static RenderEntity as WorldQuery>::State;
297
298        fn shrink_fetch<'wlong: 'wshort, 'wshort>(
299            fetch: Self::Fetch<'wlong>,
300        ) -> Self::Fetch<'wshort> {
301            fetch
302        }
303
304        #[inline]
305        unsafe fn init_fetch<'w, 's>(
306            world: UnsafeWorldCell<'w>,
307            component_id: &'s ComponentId,
308            last_run: Tick,
309            this_run: Tick,
310        ) -> Self::Fetch<'w> {
311            // SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
312            unsafe {
313                <&RenderEntity as WorldQuery>::init_fetch(world, component_id, last_run, this_run)
314            }
315        }
316
317        const IS_DENSE: bool = <&'static RenderEntity as WorldQuery>::IS_DENSE;
318
319        #[inline]
320        unsafe fn set_archetype<'w, 's>(
321            fetch: &mut Self::Fetch<'w>,
322            component_id: &'s ComponentId,
323            archetype: &'w Archetype,
324            table: &'w Table,
325        ) {
326            // SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
327            unsafe {
328                <&RenderEntity as WorldQuery>::set_archetype(fetch, component_id, archetype, table);
329            }
330        }
331
332        #[inline]
333        unsafe fn set_table<'w, 's>(
334            fetch: &mut Self::Fetch<'w>,
335            &component_id: &'s ComponentId,
336            table: &'w Table,
337        ) {
338            // SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
339            unsafe { <&RenderEntity as WorldQuery>::set_table(fetch, &component_id, table) }
340        }
341
342        fn update_component_access(&component_id: &ComponentId, access: &mut FilteredAccess) {
343            <&RenderEntity as WorldQuery>::update_component_access(&component_id, access);
344        }
345
346        fn init_state(world: &mut World) -> ComponentId {
347            <&RenderEntity as WorldQuery>::init_state(world)
348        }
349
350        fn get_state(components: &Components) -> Option<Self::State> {
351            <&RenderEntity as WorldQuery>::get_state(components)
352        }
353
354        fn matches_component_set(
355            &state: &ComponentId,
356            set_contains_id: &impl Fn(ComponentId) -> bool,
357        ) -> bool {
358            <&RenderEntity as WorldQuery>::matches_component_set(&state, set_contains_id)
359        }
360    }
361
362    // SAFETY: Component access of Self::ReadOnly is a subset of Self.
363    // Self::ReadOnly matches exactly the same archetypes/tables as Self.
364    unsafe impl QueryData for RenderEntity {
365        const IS_READ_ONLY: bool = true;
366        const IS_ARCHETYPAL: bool = <&MainEntity as QueryData>::IS_ARCHETYPAL;
367        type ReadOnly = RenderEntity;
368        type Item<'w, 's> = Entity;
369
370        fn shrink<'wlong: 'wshort, 'wshort, 's>(
371            item: Self::Item<'wlong, 's>,
372        ) -> Self::Item<'wshort, 's> {
373            item
374        }
375
376        #[inline(always)]
377        unsafe fn fetch<'w, 's>(
378            state: &'s Self::State,
379            fetch: &mut Self::Fetch<'w>,
380            entity: Entity,
381            table_row: TableRow,
382        ) -> Option<Self::Item<'w, 's>> {
383            // SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
384            let component =
385                unsafe { <&RenderEntity as QueryData>::fetch(state, fetch, entity, table_row) };
386            component.map(RenderEntity::id)
387        }
388
389        fn iter_access(
390            state: &Self::State,
391        ) -> impl Iterator<Item = bevy_ecs::query::EcsAccessType<'_>> {
392            <&RenderEntity as QueryData>::iter_access(state)
393        }
394    }
395
396    // SAFETY: the underlying `Entity` is copied, and no mutable access is provided.
397    unsafe impl ReadOnlyQueryData for RenderEntity {}
398
399    impl ArchetypeQueryData for RenderEntity {}
400
401    impl ReleaseStateQueryData for RenderEntity {
402        fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> {
403            item
404        }
405    }
406
407    /// SAFETY: defers completely to `&RenderEntity` implementation,
408    /// and then only modifies the output safely.
409    unsafe impl WorldQuery for MainEntity {
410        type Fetch<'w> = <&'static MainEntity as WorldQuery>::Fetch<'w>;
411        type State = <&'static MainEntity as WorldQuery>::State;
412
413        fn shrink_fetch<'wlong: 'wshort, 'wshort>(
414            fetch: Self::Fetch<'wlong>,
415        ) -> Self::Fetch<'wshort> {
416            fetch
417        }
418
419        #[inline]
420        unsafe fn init_fetch<'w, 's>(
421            world: UnsafeWorldCell<'w>,
422            component_id: &'s ComponentId,
423            last_run: Tick,
424            this_run: Tick,
425        ) -> Self::Fetch<'w> {
426            // SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
427            unsafe {
428                <&MainEntity as WorldQuery>::init_fetch(world, component_id, last_run, this_run)
429            }
430        }
431
432        const IS_DENSE: bool = <&'static MainEntity as WorldQuery>::IS_DENSE;
433
434        #[inline]
435        unsafe fn set_archetype<'w, 's>(
436            fetch: &mut Self::Fetch<'w>,
437            component_id: &ComponentId,
438            archetype: &'w Archetype,
439            table: &'w Table,
440        ) {
441            // SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
442            unsafe {
443                <&MainEntity as WorldQuery>::set_archetype(fetch, component_id, archetype, table);
444            }
445        }
446
447        #[inline]
448        unsafe fn set_table<'w, 's>(
449            fetch: &mut Self::Fetch<'w>,
450            &component_id: &'s ComponentId,
451            table: &'w Table,
452        ) {
453            // SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
454            unsafe { <&MainEntity as WorldQuery>::set_table(fetch, &component_id, table) }
455        }
456
457        fn update_component_access(&component_id: &ComponentId, access: &mut FilteredAccess) {
458            <&MainEntity as WorldQuery>::update_component_access(&component_id, access);
459        }
460
461        fn init_state(world: &mut World) -> ComponentId {
462            <&MainEntity as WorldQuery>::init_state(world)
463        }
464
465        fn get_state(components: &Components) -> Option<Self::State> {
466            <&MainEntity as WorldQuery>::get_state(components)
467        }
468
469        fn matches_component_set(
470            &state: &ComponentId,
471            set_contains_id: &impl Fn(ComponentId) -> bool,
472        ) -> bool {
473            <&MainEntity as WorldQuery>::matches_component_set(&state, set_contains_id)
474        }
475    }
476
477    // SAFETY: Component access of Self::ReadOnly is a subset of Self.
478    // Self::ReadOnly matches exactly the same archetypes/tables as Self.
479    unsafe impl QueryData for MainEntity {
480        const IS_READ_ONLY: bool = true;
481        const IS_ARCHETYPAL: bool = <&MainEntity as QueryData>::IS_ARCHETYPAL;
482        type ReadOnly = MainEntity;
483        type Item<'w, 's> = Entity;
484
485        fn shrink<'wlong: 'wshort, 'wshort, 's>(
486            item: Self::Item<'wlong, 's>,
487        ) -> Self::Item<'wshort, 's> {
488            item
489        }
490
491        #[inline(always)]
492        unsafe fn fetch<'w, 's>(
493            state: &'s Self::State,
494            fetch: &mut Self::Fetch<'w>,
495            entity: Entity,
496            table_row: TableRow,
497        ) -> Option<Self::Item<'w, 's>> {
498            // SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
499            let component =
500                unsafe { <&MainEntity as QueryData>::fetch(state, fetch, entity, table_row) };
501            component.map(MainEntity::id)
502        }
503
504        fn iter_access(
505            state: &Self::State,
506        ) -> impl Iterator<Item = bevy_ecs::query::EcsAccessType<'_>> {
507            <&MainEntity as QueryData>::iter_access(state)
508        }
509    }
510
511    // SAFETY: the underlying `Entity` is copied, and no mutable access is provided.
512    unsafe impl ReadOnlyQueryData for MainEntity {}
513
514    impl ArchetypeQueryData for MainEntity {}
515
516    impl ReleaseStateQueryData for MainEntity {
517        fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> {
518            item
519        }
520    }
521}
522
523#[cfg(test)]
524mod tests {
525    use bevy_ecs::{
526        component::Component,
527        entity::Entity,
528        lifecycle::{Add, Remove},
529        observer::On,
530        query::With,
531        system::{Query, ResMut},
532        world::World,
533    };
534
535    use super::{
536        entity_sync_system, EntityRecord, MainEntity, PendingSyncEntity, RenderEntity,
537        SyncToRenderWorld,
538    };
539
540    #[derive(Component)]
541    struct RenderDataComponent;
542
543    #[test]
544    fn sync_world() {
545        let mut main_world = World::new();
546        let mut render_world = World::new();
547        main_world.init_resource::<PendingSyncEntity>();
548
549        main_world.add_observer(
550            |add: On<Add, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
551                pending.push(EntityRecord::Added(add.entity));
552            },
553        );
554        main_world.add_observer(
555            |remove: On<Remove, SyncToRenderWorld>,
556             mut pending: ResMut<PendingSyncEntity>,
557             query: Query<&RenderEntity>| {
558                if let Ok(e) = query.get(remove.entity) {
559                    pending.push(EntityRecord::Removed(*e));
560                };
561            },
562        );
563
564        // spawn some empty entities for test
565        for _ in 0..99 {
566            main_world.spawn_empty();
567        }
568
569        // spawn
570        let main_entity = main_world
571            .spawn(RenderDataComponent)
572            // indicates that its entity needs to be synchronized to the render world
573            .insert(SyncToRenderWorld)
574            .id();
575
576        entity_sync_system(&mut main_world, &mut render_world);
577
578        let mut q = render_world.query_filtered::<Entity, With<MainEntity>>();
579
580        // Only one synchronized entity
581        assert!(q.iter(&render_world).count() == 1);
582
583        let render_entity = q.single(&render_world).unwrap();
584        let render_entity_component = main_world.get::<RenderEntity>(main_entity).unwrap();
585
586        assert!(render_entity_component.id() == render_entity);
587
588        let main_entity_component = render_world
589            .get::<MainEntity>(render_entity_component.id())
590            .unwrap();
591
592        assert!(main_entity_component.id() == main_entity);
593
594        // despawn
595        main_world.despawn(main_entity);
596
597        entity_sync_system(&mut main_world, &mut render_world);
598
599        // Only one synchronized entity
600        assert!(q.iter(&render_world).count() == 0);
601    }
602}