Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/sync_world.rs
6849 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::{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),
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) => {
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
256
pub(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`.
276
mod 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)]
502
mod 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
}
581
582