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#[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#[derive(Component, Copy, Clone, Debug, Default, Reflect)]
122#[reflect(Component, Default, Clone)]
123#[component(storage = "SparseSet")]
124pub struct SyncToRenderWorld;
125
126#[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
152unsafe impl EntityEquivalent for RenderEntity {}
154
155#[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
180unsafe impl EntityEquivalent for MainEntity {}
182
183pub type MainEntityHashMap<V> = HashMap<MainEntity, V, EntityHash>;
185
186pub type MainEntityHashSet = HashSet<MainEntity, EntityHash>;
188
189#[derive(Component, Copy, Clone, Debug, Default, Reflect)]
191#[reflect(Component, Default, Clone)]
192pub struct TemporaryRenderEntity;
193
194#[derive(Debug)]
196pub(crate) enum EntityRecord {
197 Added(Entity),
200 Removed(RenderEntity),
203 ComponentRemoved(Entity),
206}
207
208#[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 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 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 local.sort_unstable_by_key(|e| e.index());
267 for e in local.drain(..).rev() {
268 world.despawn(e);
269 }
270}
271
272mod 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 for _ in 0..99 {
566 main_world.spawn_empty();
567 }
568
569 let main_entity = main_world
571 .spawn(RenderDataComponent)
572 .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 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 main_world.despawn(main_entity);
596
597 entity_sync_system(&mut main_world, &mut render_world);
598
599 assert!(q.iter(&render_world).count() == 0);
601 }
602}