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        component::{ComponentId, Components, Tick},
282        entity::Entity,
283        query::{FilteredAccess, QueryData, ReadOnlyQueryData, ReleaseStateQueryData, WorldQuery},
284        storage::{Table, TableRow},
285        world::{unsafe_world_cell::UnsafeWorldCell, World},
286    };
287
288    /// SAFETY: defers completely to `&RenderEntity` implementation,
289    /// and then only modifies the output safely.
290    unsafe impl WorldQuery for RenderEntity {
291        type Fetch<'w> = <&'static RenderEntity as WorldQuery>::Fetch<'w>;
292        type State = <&'static RenderEntity as WorldQuery>::State;
293
294        fn shrink_fetch<'wlong: 'wshort, 'wshort>(
295            fetch: Self::Fetch<'wlong>,
296        ) -> Self::Fetch<'wshort> {
297            fetch
298        }
299
300        #[inline]
301        unsafe fn init_fetch<'w, 's>(
302            world: UnsafeWorldCell<'w>,
303            component_id: &'s ComponentId,
304            last_run: Tick,
305            this_run: Tick,
306        ) -> Self::Fetch<'w> {
307            // SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
308            unsafe {
309                <&RenderEntity as WorldQuery>::init_fetch(world, component_id, last_run, this_run)
310            }
311        }
312
313        const IS_DENSE: bool = <&'static RenderEntity as WorldQuery>::IS_DENSE;
314
315        #[inline]
316        unsafe fn set_archetype<'w, 's>(
317            fetch: &mut Self::Fetch<'w>,
318            component_id: &'s ComponentId,
319            archetype: &'w Archetype,
320            table: &'w Table,
321        ) {
322            // SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
323            unsafe {
324                <&RenderEntity as WorldQuery>::set_archetype(fetch, component_id, archetype, table);
325            }
326        }
327
328        #[inline]
329        unsafe fn set_table<'w, 's>(
330            fetch: &mut Self::Fetch<'w>,
331            &component_id: &'s ComponentId,
332            table: &'w Table,
333        ) {
334            // SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`.
335            unsafe { <&RenderEntity as WorldQuery>::set_table(fetch, &component_id, table) }
336        }
337
338        fn update_component_access(&component_id: &ComponentId, access: &mut FilteredAccess) {
339            <&RenderEntity as WorldQuery>::update_component_access(&component_id, access);
340        }
341
342        fn init_state(world: &mut World) -> ComponentId {
343            <&RenderEntity as WorldQuery>::init_state(world)
344        }
345
346        fn get_state(components: &Components) -> Option<Self::State> {
347            <&RenderEntity as WorldQuery>::get_state(components)
348        }
349
350        fn matches_component_set(
351            &state: &ComponentId,
352            set_contains_id: &impl Fn(ComponentId) -> bool,
353        ) -> bool {
354            <&RenderEntity as WorldQuery>::matches_component_set(&state, set_contains_id)
355        }
356    }
357
358    // SAFETY: Component access of Self::ReadOnly is a subset of Self.
359    // Self::ReadOnly matches exactly the same archetypes/tables as Self.
360    unsafe impl QueryData for RenderEntity {
361        const IS_READ_ONLY: bool = true;
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        ) -> 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.id()
382        }
383    }
384
385    // SAFETY: the underlying `Entity` is copied, and no mutable access is provided.
386    unsafe impl ReadOnlyQueryData for RenderEntity {}
387
388    impl ReleaseStateQueryData for RenderEntity {
389        fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> {
390            item
391        }
392    }
393
394    /// SAFETY: defers completely to `&RenderEntity` implementation,
395    /// and then only modifies the output safely.
396    unsafe impl WorldQuery for MainEntity {
397        type Fetch<'w> = <&'static MainEntity as WorldQuery>::Fetch<'w>;
398        type State = <&'static MainEntity as WorldQuery>::State;
399
400        fn shrink_fetch<'wlong: 'wshort, 'wshort>(
401            fetch: Self::Fetch<'wlong>,
402        ) -> Self::Fetch<'wshort> {
403            fetch
404        }
405
406        #[inline]
407        unsafe fn init_fetch<'w, 's>(
408            world: UnsafeWorldCell<'w>,
409            component_id: &'s ComponentId,
410            last_run: Tick,
411            this_run: Tick,
412        ) -> Self::Fetch<'w> {
413            // SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
414            unsafe {
415                <&MainEntity as WorldQuery>::init_fetch(world, component_id, last_run, this_run)
416            }
417        }
418
419        const IS_DENSE: bool = <&'static MainEntity as WorldQuery>::IS_DENSE;
420
421        #[inline]
422        unsafe fn set_archetype<'w, 's>(
423            fetch: &mut Self::Fetch<'w>,
424            component_id: &ComponentId,
425            archetype: &'w Archetype,
426            table: &'w Table,
427        ) {
428            // SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
429            unsafe {
430                <&MainEntity as WorldQuery>::set_archetype(fetch, component_id, archetype, table);
431            }
432        }
433
434        #[inline]
435        unsafe fn set_table<'w, 's>(
436            fetch: &mut Self::Fetch<'w>,
437            &component_id: &'s ComponentId,
438            table: &'w Table,
439        ) {
440            // SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
441            unsafe { <&MainEntity as WorldQuery>::set_table(fetch, &component_id, table) }
442        }
443
444        fn update_component_access(&component_id: &ComponentId, access: &mut FilteredAccess) {
445            <&MainEntity as WorldQuery>::update_component_access(&component_id, access);
446        }
447
448        fn init_state(world: &mut World) -> ComponentId {
449            <&MainEntity as WorldQuery>::init_state(world)
450        }
451
452        fn get_state(components: &Components) -> Option<Self::State> {
453            <&MainEntity as WorldQuery>::get_state(components)
454        }
455
456        fn matches_component_set(
457            &state: &ComponentId,
458            set_contains_id: &impl Fn(ComponentId) -> bool,
459        ) -> bool {
460            <&MainEntity as WorldQuery>::matches_component_set(&state, set_contains_id)
461        }
462    }
463
464    // SAFETY: Component access of Self::ReadOnly is a subset of Self.
465    // Self::ReadOnly matches exactly the same archetypes/tables as Self.
466    unsafe impl QueryData for MainEntity {
467        const IS_READ_ONLY: bool = true;
468        type ReadOnly = MainEntity;
469        type Item<'w, 's> = Entity;
470
471        fn shrink<'wlong: 'wshort, 'wshort, 's>(
472            item: Self::Item<'wlong, 's>,
473        ) -> Self::Item<'wshort, 's> {
474            item
475        }
476
477        #[inline(always)]
478        unsafe fn fetch<'w, 's>(
479            state: &'s Self::State,
480            fetch: &mut Self::Fetch<'w>,
481            entity: Entity,
482            table_row: TableRow,
483        ) -> Self::Item<'w, 's> {
484            // SAFETY: defers to the `&T` implementation, with T set to `MainEntity`.
485            let component =
486                unsafe { <&MainEntity as QueryData>::fetch(state, fetch, entity, table_row) };
487            component.id()
488        }
489    }
490
491    // SAFETY: the underlying `Entity` is copied, and no mutable access is provided.
492    unsafe impl ReadOnlyQueryData for MainEntity {}
493
494    impl ReleaseStateQueryData for MainEntity {
495        fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> {
496            item
497        }
498    }
499}
500
501#[cfg(test)]
502mod tests {
503    use bevy_ecs::{
504        component::Component,
505        entity::Entity,
506        lifecycle::{Add, Remove},
507        observer::On,
508        query::With,
509        system::{Query, ResMut},
510        world::World,
511    };
512
513    use super::{
514        entity_sync_system, EntityRecord, MainEntity, PendingSyncEntity, RenderEntity,
515        SyncToRenderWorld,
516    };
517
518    #[derive(Component)]
519    struct RenderDataComponent;
520
521    #[test]
522    fn sync_world() {
523        let mut main_world = World::new();
524        let mut render_world = World::new();
525        main_world.init_resource::<PendingSyncEntity>();
526
527        main_world.add_observer(
528            |add: On<Add, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
529                pending.push(EntityRecord::Added(add.entity));
530            },
531        );
532        main_world.add_observer(
533            |remove: On<Remove, SyncToRenderWorld>,
534             mut pending: ResMut<PendingSyncEntity>,
535             query: Query<&RenderEntity>| {
536                if let Ok(e) = query.get(remove.entity) {
537                    pending.push(EntityRecord::Removed(*e));
538                };
539            },
540        );
541
542        // spawn some empty entities for test
543        for _ in 0..99 {
544            main_world.spawn_empty();
545        }
546
547        // spawn
548        let main_entity = main_world
549            .spawn(RenderDataComponent)
550            // indicates that its entity needs to be synchronized to the render world
551            .insert(SyncToRenderWorld)
552            .id();
553
554        entity_sync_system(&mut main_world, &mut render_world);
555
556        let mut q = render_world.query_filtered::<Entity, With<MainEntity>>();
557
558        // Only one synchronized entity
559        assert!(q.iter(&render_world).count() == 1);
560
561        let render_entity = q.single(&render_world).unwrap();
562        let render_entity_component = main_world.get::<RenderEntity>(main_entity).unwrap();
563
564        assert!(render_entity_component.id() == render_entity);
565
566        let main_entity_component = render_world
567            .get::<MainEntity>(render_entity_component.id())
568            .unwrap();
569
570        assert!(main_entity_component.id() == main_entity);
571
572        // despawn
573        main_world.despawn(main_entity);
574
575        entity_sync_system(&mut main_world, &mut render_world);
576
577        // Only one synchronized entity
578        assert!(q.iter(&render_world).count() == 0);
579    }
580}