bevy_render/
sync_world.rs

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